1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use std::collections::HashMap;
use dashmap::DashMap;
use tokio::time::Instant;
use rlink::utils::date_time::current_timestamp;
use rlink::utils::http::client::get;

lazy_static! {
    // ip and cmdbInfo config
    static ref GLOBAL_IP_MAPPING: DashMap<String, Vec<IpMappingItem>> = DashMap::new();
    // cmdbId and ip config
    static ref GLOBAL_CMDB_ID_IP_MAP: DashMap<String, String> = DashMap::new();
}

pub fn load_ip_mapping_task(url: &str) {
    let url = url.to_string();
    tokio::runtime::Runtime::new()
        .unwrap()
        .block_on(load_remote_ip_mapping(url.as_str()));

    // 每天凌晨4点取全量IpMapping数据
    let now = current_timestamp().as_secs();
    let period = 24 * 60 * 60;
    let diff = (now / period + 1) * period - 4 * 60 * 60 - now;
    let start = Instant::now() + std::time::Duration::from_secs(diff);

    std::thread::spawn(move || {
        tokio::runtime::Runtime::new().unwrap().block_on(async {
            let mut interval = tokio::time::interval_at(start, std::time::Duration::from_secs(period));
            loop {
                interval.tick().await;

                load_remote_ip_mapping(url.as_str()).await;
            }
        });
    });
}

pub fn get_ip_mapping_config(ip: &str) -> Vec<IpMappingItem> {
    let config: &DashMap<String, Vec<IpMappingItem>> = &*GLOBAL_IP_MAPPING;
    match config.get(ip) {
        Some(conf) => {
            (*conf).clone()
        }
        None => {
            Vec::new()
        }
    }
}

fn update_ip_mapping_config(conf: HashMap<String, Vec<IpMappingItem>>) {
    let ip_mapping_config: &DashMap<String, Vec<IpMappingItem>> = &*GLOBAL_IP_MAPPING;
    let cmdb_id_ip_config: &DashMap<String, String> = &*GLOBAL_CMDB_ID_IP_MAP;
    let mut count = 0;
    for (ip, vec) in conf {
        for item in &vec {
            cmdb_id_ip_config.insert(item.id.clone(), ip.clone());
        }
        ip_mapping_config.insert(ip, vec);
        count += 1;
    }
    info!("update ip mapping config,size={}", count);
}

pub fn update_ip_mapping_by_id(item: IpMappingItem, is_del: bool) {
    let ip_mapping_config: &DashMap<String, Vec<IpMappingItem>> = &*GLOBAL_IP_MAPPING;
    let cmdb_id_ip_config: &DashMap<String, String> = &*GLOBAL_CMDB_ID_IP_MAP;
    let cmdb_id = item.id.clone();
    match cmdb_id_ip_config.get(cmdb_id.as_str()) {
        Some(ip_conf) => {
            let ip = (*ip_conf).clone();
            if is_del {
                info!("remove ip mapping:{}", ip);
                ip_mapping_config.remove(ip.as_str());
            } else {
                info!("update ip mapping:{}-{:?}", ip, item);
                let vec = match ip_mapping_config.get(ip.as_str()) {
                    Some(config) => {
                        (*config).clone()
                    }
                    None => {
                        vec![item]
                    }
                };
                ip_mapping_config.insert(ip, vec);
            }
        }
        None => {
            if item.primary_ip.is_some() {
                let ip = item.primary_ip.as_ref().unwrap();
                info!("add ip mapping:{}-{:?}", ip, item);
                ip_mapping_config.insert(ip.to_string(), vec![item]);
            };
        }
    }
}

async fn load_remote_ip_mapping(url: &str) {
    match get(url).await {
        Ok(context) => {
            info!("load ip mapping conf");
            let conf = parse_conf(context);
            update_ip_mapping_config(conf);
        }
        Err(e) => error!("get ip mapping config error. {}", e),
    }
}

fn parse_conf(context: String) -> HashMap<String, Vec<IpMappingItem>> {
    match parse_context(context) {
        Ok(map) => {
            map
        }
        Err(e) => {
            error!("ip mapping config parse error.{}", e);
            HashMap::new()
        }
    }
}

fn parse_context(context: String) -> serde_json::Result<HashMap<String, Vec<IpMappingItem>>> {
    let response: IpMappingResponse = serde_json::from_str(context.as_str())?;
    Ok(response.result)
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct IpMappingResponse {
    code: i8,
    result: HashMap<String, Vec<IpMappingItem>>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IpMappingItem {
    pub id: String,
    #[serde(rename = "primaryIp")]
    pub primary_ip: Option<String>,
    #[serde(rename = "otherIp")]
    pub other_ip: Option<String>,
    #[serde(rename = "appUk")]
    pub app_uk: Option<String>,
    #[serde(rename = "groupEnvironment")]
    pub group_environment: Option<String>,
    #[serde(rename = "logicIdcUk")]
    pub logical_idc_uk: Option<String>,
    #[serde(rename = "areaUk")]
    pub area_uk: Option<String>,
    #[serde(rename = "port")]
    pub port: Option<String>,
}

impl IpMappingItem {
    pub fn new() -> Self {
        IpMappingItem {
            id: String::new(),
            primary_ip: None,
            other_ip: None,
            app_uk: None,
            group_environment: None,
            logical_idc_uk: None,
            area_uk: None,
            port: None,
        }
    }
}