13 logging.basicConfig(filename="/tmp/rift_ns_add_corp.log", level=logging.DEBUG)
14 logger = logging.getLogger()
16 ch = logging.StreamHandler()
17 ch.setLevel(logging.INFO)
19 # create formatter and add it to the handlers
20 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
21 ch.setFormatter(formatter)
27 class JujuActionError(Exception):
31 class JujuClient(object):
32 """Class for executing Juju actions """
33 def __init__(self, ip, port, user, passwd):
39 endpoint = 'wss://%s:%d' % (ip, port)
40 logger.debug("Using endpoint=%s", endpoint)
43 self.env = jujuclient.Environment(endpoint)
44 self.env.login(passwd, user)
46 def get_service(self, name):
47 return self.env.get_service(name)
49 def _get_units(self, name):
51 Get the units associated with service
53 units = self.env.status(name)['Services'][name]['Units']
54 units = list(units.keys())
56 # convert to a friendly format for juju-python-client
57 units[:] = [('unit-%s' % u).replace('/', '-') for u in units]
60 def exec_action(self, name, action_name, params, block=False):
61 logger.debug("execute actiion %s using params %s", action_name, params)
65 actions = jujuclient.Actions(self.env)
66 results = actions.enqueue_units(self._get_units(name),
72 if 'error' in results['results'][0].keys():
73 raise JujuActionError("Juju action error: %s" % results['results'][0])
75 action = results['results'][0]['action']
76 info = actions.info([action])
78 logging.debug("Initial action results: %s", results['results'][0])
79 while info['results'][0]['status'] not in ['completed', 'failed']:
81 info = actions.info([action])
83 # break out if the action doesn't complete in 10 secs
86 raise JujuActionError("Juju action timed out after 30 seconds")
88 if info['results'][0]['status'] != 'completed':
89 raise JujuActionError("Action %s failure: %s" % (action_name, info['results'][0]))
94 class CharmAction(object):
95 def __init__(self, deployed_name, action_name, action_params=None):
96 self._deployed_name = deployed_name
97 self._action_name = action_name
98 self._params = action_params if action_params is not None else []
100 def execute(self, juju_client):
101 logger.info("Executing charm (%s) action (%s) with params (%s)",
102 self._deployed_name, self._action_name, self._params)
104 info = juju_client.exec_action(
105 name=self._deployed_name,
106 action_name=self._action_name,
111 except JujuActionError as e:
112 logger.error("Juju charm (%s) action (%s) failed: %s",
113 self._deployed_name, self._action_name, str(e))
116 logger.debug("Juju charm (%s) action (%s) success.",
117 self._deployed_name, self._action_name)
120 class DeployedProxyCharm(object):
121 def __init__(self, juju_client, service_name, mgmt_ip=None, charm_name=None):
122 self._juju_client = juju_client
123 self.service_name = service_name
124 self.mgmt_ip = mgmt_ip
125 self.charm_name = charm_name
127 def do_action(self, action_name, action_params={}):
128 action = CharmAction(self.service_name, action_name, action_params)
129 action.execute(self._juju_client)
132 class SixWindPEProxyCharm(DeployedProxyCharm):
136 def configure_interface(self, iface_name, ipv4_interface_str=None):
137 action = "configure-interface"
138 params = {'iface-name', iface_name}
140 if ipv4_interface_str is None:
141 # Use ipaddress module to validate ipv4 interface string
142 ip_intf = ipaddress.IPv4Interface(ipv4_interface_str)
143 params["cidr"] = ip_intf.with_prefixlen
145 self.do_action(action, params)
147 self.do_action(action, params)
150 def add_corporation(self, domain_name, user_iface_name, vlan_id, corp_gw,
151 corp_net, local_net="10.255.255.0/24", local_net_area="0"):
152 logger.debug("Add corporation called with params: %s", locals())
154 action = "add-corporation"
156 "domain-name": domain_name,
157 "iface-name": user_iface_name,
158 "vlan-id": int(vlan_id),
161 "subnet-cidr":local_net,
162 "subnet-area":local_net_area,
165 self.do_action(action, params)
167 def connect_domains(self, domain_name, core_iface_name, local_ip, remote_ip,
168 internal_local_ip, internal_remote_ip, tunnel_name,
169 tunnel_key, tunnel_type="gre"):
171 logger.debug("Connect domains called with params: %s", locals())
173 action = "connect-domains"
175 "domain-name": domain_name,
176 "iface-name": core_iface_name,
177 "tunnel-name": tunnel_name,
178 "local-ip": local_ip,
179 "remote-ip": remote_ip,
180 "tunnel-key": tunnel_key,
181 "internal-local-ip": internal_local_ip,
182 "internal-remote-ip": internal_remote_ip,
183 "tunnel-type":tunnel_type,
186 self.do_action(action, params)
189 class PEGroupConfig(object):
190 def __init__(self, pe_group_cfg):
191 self._pe_group_cfg = pe_group_cfg
193 def _get_param_value(self, param_name):
194 for param in self._pe_group_cfg["parameter"]:
195 if param["name"] == param_name:
196 return param["value"]
198 raise ValueError("PE param not found: %s" % param_name)
202 return self._get_param_value("Vlan ID")
205 def interface_name(self):
206 return self._get_param_value("Interface Name")
209 def corp_network(self):
210 return self._get_param_value("Corp. Network")
213 def corp_gateway(self):
214 return self._get_param_value("Corp. Gateway")
217 class AddCorporationRequest(object):
218 def __init__(self, add_corporation_rpc):
219 self._add_corporation_rpc = add_corporation_rpc
223 return self._add_corporation_rpc["name"]
226 def param_groups(self):
227 return self._add_corporation_rpc["parameter_group"]
231 return self._add_corporation_rpc["parameter"]
234 def corporation_name(self):
235 for param in self.params:
236 if param["name"] == "Corporation Name":
237 return param["value"]
239 raise ValueError("Could not find 'Corporation Name' field")
242 def tunnel_key(self):
243 for param in self.params:
244 if param["name"] == "Tunnel Key":
245 return param["value"]
247 raise ValueError("Could not find 'Tunnel Key' field")
249 def get_pe_parameter_group_map(self):
251 for group in self.param_groups:
252 group_name_map[group["name"]] = group
254 return group_name_map
256 def get_parameter_name_map(self):
258 for param in self.params:
259 name_param_map[param["name"]] = param
261 return name_param_map
264 def from_yaml_cfg(cls, yaml_hdl):
265 config = yaml.load(yaml_hdl)
271 class JujuVNFConfig(object):
272 def __init__(self, vnfr_index_map, vnf_name_map, vnf_init_config_map):
273 self._vnfr_index_map = vnfr_index_map
274 self._vnf_name_map = vnf_name_map
275 self._vnf_init_config_map = vnf_name_map
277 def get_service_name(self, vnf_index):
278 for vnfr_id, index in self._vnfr_index_map.items():
279 if index != vnf_index:
282 return self._vnf_name_map[vnfr_id]
284 raise ValueError("VNF Index not found: %s" % vnf_index)
286 def get_vnfr_id(self, vnf_index):
287 for vnfr_id, index in self._vnfr_index_map.items():
288 if index != vnf_index:
293 raise ValueError("VNF Index not found: %s" % vnf_index)
296 def from_yaml_cfg(cls, yaml_hdl):
297 config = yaml.load(yaml_hdl)
299 config["vnfr_index_map"],
300 config["unit_names"],
301 config["init_config"],
305 class JujuClientConfig(object):
306 def __init__(self, juju_ctrl_cfg):
307 self._juju_ctrl_cfg = juju_ctrl_cfg
311 return self._juju_ctrl_cfg["name"]
315 return self._juju_ctrl_cfg["host"]
319 return self._juju_ctrl_cfg["port"]
323 return self._juju_ctrl_cfg["user"]
327 return self._juju_ctrl_cfg["secret"]
330 def from_yaml_cfg(cls, yaml_hdl):
331 config = yaml.load(yaml_hdl)
333 config["config_agent"],
337 class OSM_MWC_Demo(object):
338 VNF_INDEX_NAME_MAP = {
350 "internal_local_ip": "10.255.255.1"
356 "internal_local_ip": "10.255.255.1"
364 "internal_local_ip": "10.255.255.2"
370 "internal_local_ip": "10.255.255.2"
378 "internal_local_ip": "10.255.255.3"
384 "internal_local_ip": "10.255.255.3"
390 def get_pe_vnf_index(pe_name):
391 if pe_name not in OSM_MWC_Demo.VNF_INDEX_NAME_MAP:
392 raise ValueError("Could not find PE name: %s", pe_name)
394 return OSM_MWC_Demo.VNF_INDEX_NAME_MAP[pe_name]
397 def get_src_core_iface(src_pe_name, dest_pe_name):
398 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ifacename"]
401 def get_local_ip(src_pe_name, dest_pe_name):
402 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ip"]
405 def get_remote_ip(src_pe_name, dest_pe_name):
406 return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["ip"]
409 def get_internal_local_ip(src_pe_name, dest_pe_name):
410 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["internal_local_ip"]
413 def get_internal_remote_ip(src_pe_name, dest_pe_name):
414 return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["internal_local_ip"]
417 def add_pe_corporation(src_pe_name, src_pe_charm, src_pe_group_cfg, corporation_name):
418 domain_name = corporation_name
419 vlan_id = src_pe_group_cfg.vlan_id
420 corp_gw = src_pe_group_cfg.corp_gateway
421 corp_net = src_pe_group_cfg.corp_network
423 user_iface = src_pe_group_cfg.interface_name
425 src_pe_charm.add_corporation(domain_name, user_iface, vlan_id, corp_gw, corp_net)
428 def connect_pe_domains(src_pe_name, src_pe_charm, dest_pe_name, corporation_name, tunnel_key):
429 domain_name = corporation_name
430 core_iface_name = OSM_MWC_Demo.get_src_core_iface(src_pe_name, dest_pe_name)
431 local_ip = OSM_MWC_Demo.get_local_ip(src_pe_name, dest_pe_name)
432 remote_ip = OSM_MWC_Demo.get_remote_ip(src_pe_name, dest_pe_name)
433 internal_local_ip = OSM_MWC_Demo.get_internal_local_ip(src_pe_name, dest_pe_name)
434 internal_remote_ip = OSM_MWC_Demo.get_internal_remote_ip(src_pe_name, dest_pe_name)
437 src_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(src_pe_name)
438 dest_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(dest_pe_name)
440 # Create a 4 digit hash of the corporation name
441 hash_object = hashlib.md5(corporation_name.encode())
442 corp_hash = hash_object.hexdigest()[-4:]
444 # Tunnel name is the 4 digit corporation name hash followed by
445 # src index and dest index. When there are less than 10 PE's
446 # this creates a 8 character tunnel name which is the limit.
447 tunnel_name = "".join([corp_hash, "_", str(src_pe_idx), str(dest_pe_idx)])
449 src_pe_charm.connect_domains(domain_name, core_iface_name, local_ip, remote_ip,
450 internal_local_ip, internal_remote_ip, tunnel_name,
454 def main(argv=sys.argv[1:]):
455 parser = argparse.ArgumentParser()
456 parser.add_argument("yaml_cfg_file", type=argparse.FileType('r'))
457 parser.add_argument("--dry-run", action="store_true")
458 parser.add_argument("--quiet", "-q", dest="verbose", action="store_false")
459 args = parser.parse_args()
461 ch.setLevel(logging.DEBUG)
464 dry_run = args.dry_run
466 yaml_str = args.yaml_cfg_file.read()
468 juju_cfg = JujuClientConfig.from_yaml_cfg(yaml_str)
469 juju_client = JujuClient(juju_cfg.host, juju_cfg.port, juju_cfg.user, juju_cfg.secret)
471 juju_vnf_config = JujuVNFConfig.from_yaml_cfg(yaml_str)
473 rpc_request = AddCorporationRequest.from_yaml_cfg(yaml_str)
474 pe_param_group_map = rpc_request.get_pe_parameter_group_map()
476 pe_name_charm_map = {}
477 for pe_name, pe_group_cfg in pe_param_group_map.items():
478 # The PE name (i.e. PE1) must be in the parameter group name so we can correlate
479 # to an actual VNF in the descriptor.
480 pe_vnf_index = OSM_MWC_Demo.get_pe_vnf_index(pe_name)
482 # Get the deployed VNFR charm service name
483 pe_charm_service_name = juju_vnf_config.get_service_name(pe_vnf_index)
485 pe_name_charm_map[pe_name] = SixWindPEProxyCharm(juju_client, pe_charm_service_name)
487 # At this point we have SixWindPEProxyCharm() instances for each PE and each
488 # PE param group configuration.
489 for src_pe_name in pe_param_group_map:
491 src_pe_name=src_pe_name,
492 src_pe_charm=pe_name_charm_map[src_pe_name],
493 src_pe_group_cfg=PEGroupConfig(pe_param_group_map[src_pe_name]),
494 corporation_name=rpc_request.corporation_name
497 # Create a permutation of all PE's involved in this topology and connect
498 # them together by creating tunnels with matching keys
499 for src_pe_name, dest_pe_name in itertools.permutations(pe_name_charm_map, 2):
501 src_pe_name=src_pe_name,
502 src_pe_charm=pe_name_charm_map[src_pe_name],
503 dest_pe_name=dest_pe_name,
504 corporation_name=rpc_request.corporation_name,
505 tunnel_key=rpc_request.tunnel_key,
508 if __name__ == "__main__":
511 except Exception as e:
512 logger.exception("Caught exception when executing add_corporation ns")