3 # Copyright 2016 RIFT.IO Inc
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
28 logging.basicConfig(filename="/tmp/rift_ns_add_corp.log", level=logging.DEBUG)
29 logger = logging.getLogger()
31 ch = logging.StreamHandler()
32 ch.setLevel(logging.INFO)
34 # create formatter and add it to the handlers
35 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
36 ch.setFormatter(formatter)
42 class JujuActionError(Exception):
46 class JujuClient(object):
47 """Class for executing Juju actions """
48 def __init__(self, ip, port, user, passwd):
54 endpoint = 'wss://%s:%d' % (ip, port)
55 logger.debug("Using endpoint=%s", endpoint)
58 self.env = jujuclient.Environment(endpoint)
59 self.env.login(passwd, user)
61 def get_service(self, name):
62 return self.env.get_service(name)
64 def _get_units(self, name):
66 Get the units associated with service
68 units = self.env.status(name)['Services'][name]['Units']
69 units = list(units.keys())
71 # convert to a friendly format for juju-python-client
72 units[:] = [('unit-%s' % u).replace('/', '-') for u in units]
75 def exec_action(self, name, action_name, params, block=False):
76 logger.debug("execute actiion %s using params %s", action_name, params)
80 actions = jujuclient.Actions(self.env)
81 results = actions.enqueue_units(self._get_units(name),
87 if 'error' in results['results'][0].keys():
88 raise JujuActionError("Juju action error: %s" % results['results'][0])
90 action = results['results'][0]['action']
91 info = actions.info([action])
93 logging.debug("Initial action results: %s", results['results'][0])
94 while info['results'][0]['status'] not in ['completed', 'failed']:
96 info = actions.info([action])
98 # break out if the action doesn't complete in 10 secs
101 raise JujuActionError("Juju action timed out after 30 seconds")
103 if info['results'][0]['status'] != 'completed':
104 raise JujuActionError("Action %s failure: %s" % (action_name, info['results'][0]))
109 class CharmAction(object):
110 def __init__(self, deployed_name, action_name, action_params=None):
111 self._deployed_name = deployed_name
112 self._action_name = action_name
113 self._params = action_params if action_params is not None else []
115 def execute(self, juju_client):
116 logger.info("Executing charm (%s) action (%s) with params (%s)",
117 self._deployed_name, self._action_name, self._params)
119 info = juju_client.exec_action(
120 name=self._deployed_name,
121 action_name=self._action_name,
126 except JujuActionError as e:
127 logger.error("Juju charm (%s) action (%s) failed: %s",
128 self._deployed_name, self._action_name, str(e))
131 logger.debug("Juju charm (%s) action (%s) success.",
132 self._deployed_name, self._action_name)
135 class DeployedProxyCharm(object):
136 def __init__(self, juju_client, service_name, mgmt_ip=None, charm_name=None):
137 self._juju_client = juju_client
138 self.service_name = service_name
139 self.mgmt_ip = mgmt_ip
140 self.charm_name = charm_name
142 def do_action(self, action_name, action_params={}):
143 action = CharmAction(self.service_name, action_name, action_params)
144 action.execute(self._juju_client)
147 class SixWindPEProxyCharm(DeployedProxyCharm):
151 def configure_interface(self, iface_name, ipv4_interface_str=None):
152 action = "configure-interface"
153 params = {'iface-name', iface_name}
155 if ipv4_interface_str is None:
156 # Use ipaddress module to validate ipv4 interface string
157 ip_intf = ipaddress.IPv4Interface(ipv4_interface_str)
158 params["cidr"] = ip_intf.with_prefixlen
160 self.do_action(action, params)
162 self.do_action(action, params)
165 def add_corporation(self, domain_name, user_iface_name, vlan_id, corp_gw,
166 corp_net, local_net="10.255.255.0/24", local_net_area="0"):
167 logger.debug("Add corporation called with params: %s", locals())
169 action = "add-corporation"
171 "domain-name": domain_name,
172 "iface-name": user_iface_name,
173 "vlan-id": int(vlan_id),
176 "subnet-cidr":local_net,
177 "subnet-area":local_net_area,
180 self.do_action(action, params)
182 def connect_domains(self, domain_name, core_iface_name, local_ip, remote_ip,
183 internal_local_ip, internal_remote_ip, tunnel_name,
184 tunnel_key, tunnel_type="gre"):
186 logger.debug("Connect domains called with params: %s", locals())
188 action = "connect-domains"
190 "domain-name": domain_name,
191 "iface-name": core_iface_name,
192 "tunnel-name": tunnel_name,
193 "local-ip": local_ip,
194 "remote-ip": remote_ip,
195 "tunnel-key": tunnel_key,
196 "internal-local-ip": internal_local_ip,
197 "internal-remote-ip": internal_remote_ip,
198 "tunnel-type":tunnel_type,
201 self.do_action(action, params)
204 class PEGroupConfig(object):
205 def __init__(self, pe_group_cfg):
206 self._pe_group_cfg = pe_group_cfg
208 def _get_param_value(self, param_name):
209 for param in self._pe_group_cfg["parameter"]:
210 if param["name"] == param_name:
211 return param["value"]
213 raise ValueError("PE param not found: %s" % param_name)
217 return self._get_param_value("Vlan ID")
220 def interface_name(self):
221 return self._get_param_value("Interface Name")
224 def corp_network(self):
225 return self._get_param_value("Corp. Network")
228 def corp_gateway(self):
229 return self._get_param_value("Corp. Gateway")
232 class AddCorporationRequest(object):
233 def __init__(self, add_corporation_rpc):
234 self._add_corporation_rpc = add_corporation_rpc
238 return self._add_corporation_rpc["name"]
241 def param_groups(self):
242 return self._add_corporation_rpc["parameter_group"]
246 return self._add_corporation_rpc["parameter"]
249 def corporation_name(self):
250 for param in self.params:
251 if param["name"] == "Corporation Name":
252 return param["value"]
254 raise ValueError("Could not find 'Corporation Name' field")
257 def tunnel_key(self):
258 for param in self.params:
259 if param["name"] == "Tunnel Key":
260 return param["value"]
262 raise ValueError("Could not find 'Tunnel Key' field")
264 def get_pe_parameter_group_map(self):
266 for group in self.param_groups:
267 group_name_map[group["name"]] = group
269 return group_name_map
271 def get_parameter_name_map(self):
273 for param in self.params:
274 name_param_map[param["name"]] = param
276 return name_param_map
279 def from_yaml_cfg(cls, yaml_hdl):
280 config = yaml.load(yaml_hdl)
286 class JujuVNFConfig(object):
287 def __init__(self, vnfr_index_map, vnf_name_map, vnf_init_config_map):
288 self._vnfr_index_map = vnfr_index_map
289 self._vnf_name_map = vnf_name_map
290 self._vnf_init_config_map = vnf_name_map
292 def get_service_name(self, vnf_index):
293 for vnfr_id, index in self._vnfr_index_map.items():
294 if index != vnf_index:
297 return self._vnf_name_map[vnfr_id]
299 raise ValueError("VNF Index not found: %s" % vnf_index)
301 def get_vnfr_id(self, vnf_index):
302 for vnfr_id, index in self._vnfr_index_map.items():
303 if index != vnf_index:
308 raise ValueError("VNF Index not found: %s" % vnf_index)
311 def from_yaml_cfg(cls, yaml_hdl):
312 config = yaml.load(yaml_hdl)
314 config["vnfr_index_map"],
315 config["unit_names"],
316 config["init_config"],
320 class JujuClientConfig(object):
321 def __init__(self, juju_ctrl_cfg):
322 self._juju_ctrl_cfg = juju_ctrl_cfg
326 return self._juju_ctrl_cfg["name"]
330 return self._juju_ctrl_cfg["host"]
334 return self._juju_ctrl_cfg["port"]
338 return self._juju_ctrl_cfg["user"]
342 return self._juju_ctrl_cfg["secret"]
345 def from_yaml_cfg(cls, yaml_hdl):
346 config = yaml.load(yaml_hdl)
348 config["config_agent"],
352 class OSM_MWC_Demo(object):
353 VNF_INDEX_NAME_MAP = {
365 "internal_local_ip": "10.255.255.1"
371 "internal_local_ip": "10.255.255.1"
379 "internal_local_ip": "10.255.255.2"
385 "internal_local_ip": "10.255.255.2"
393 "internal_local_ip": "10.255.255.3"
399 "internal_local_ip": "10.255.255.3"
405 def get_pe_vnf_index(pe_name):
406 if pe_name not in OSM_MWC_Demo.VNF_INDEX_NAME_MAP:
407 raise ValueError("Could not find PE name: %s", pe_name)
409 return OSM_MWC_Demo.VNF_INDEX_NAME_MAP[pe_name]
412 def get_src_core_iface(src_pe_name, dest_pe_name):
413 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ifacename"]
416 def get_local_ip(src_pe_name, dest_pe_name):
417 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["ip"]
420 def get_remote_ip(src_pe_name, dest_pe_name):
421 return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["ip"]
424 def get_internal_local_ip(src_pe_name, dest_pe_name):
425 return OSM_MWC_Demo.CORE_PE_CONN_MAP[src_pe_name][dest_pe_name]["internal_local_ip"]
428 def get_internal_remote_ip(src_pe_name, dest_pe_name):
429 return OSM_MWC_Demo.CORE_PE_CONN_MAP[dest_pe_name][src_pe_name]["internal_local_ip"]
432 def add_pe_corporation(src_pe_name, src_pe_charm, src_pe_group_cfg, corporation_name):
433 domain_name = corporation_name
434 vlan_id = src_pe_group_cfg.vlan_id
435 corp_gw = src_pe_group_cfg.corp_gateway
436 corp_net = src_pe_group_cfg.corp_network
438 user_iface = src_pe_group_cfg.interface_name
440 src_pe_charm.add_corporation(domain_name, user_iface, vlan_id, corp_gw, corp_net)
443 def connect_pe_domains(src_pe_name, src_pe_charm, dest_pe_name, corporation_name, tunnel_key):
444 domain_name = corporation_name
445 core_iface_name = OSM_MWC_Demo.get_src_core_iface(src_pe_name, dest_pe_name)
446 local_ip = OSM_MWC_Demo.get_local_ip(src_pe_name, dest_pe_name)
447 remote_ip = OSM_MWC_Demo.get_remote_ip(src_pe_name, dest_pe_name)
448 internal_local_ip = OSM_MWC_Demo.get_internal_local_ip(src_pe_name, dest_pe_name)
449 internal_remote_ip = OSM_MWC_Demo.get_internal_remote_ip(src_pe_name, dest_pe_name)
452 src_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(src_pe_name)
453 dest_pe_idx = OSM_MWC_Demo.get_pe_vnf_index(dest_pe_name)
455 # Create a 4 digit hash of the corporation name
456 hash_object = hashlib.md5(corporation_name.encode())
457 corp_hash = hash_object.hexdigest()[-4:]
459 # Tunnel name is the 4 digit corporation name hash followed by
460 # src index and dest index. When there are less than 10 PE's
461 # this creates a 8 character tunnel name which is the limit.
462 tunnel_name = "".join([corp_hash, "_", str(src_pe_idx), str(dest_pe_idx)])
464 src_pe_charm.connect_domains(domain_name, core_iface_name, local_ip, remote_ip,
465 internal_local_ip, internal_remote_ip, tunnel_name,
469 def main(argv=sys.argv[1:]):
470 parser = argparse.ArgumentParser()
471 parser.add_argument("yaml_cfg_file", type=argparse.FileType('r'))
472 parser.add_argument("--dry-run", action="store_true")
473 parser.add_argument("--quiet", "-q", dest="verbose", action="store_false")
474 args = parser.parse_args()
476 ch.setLevel(logging.DEBUG)
479 dry_run = args.dry_run
481 yaml_str = args.yaml_cfg_file.read()
483 juju_cfg = JujuClientConfig.from_yaml_cfg(yaml_str)
484 juju_client = JujuClient(juju_cfg.host, juju_cfg.port, juju_cfg.user, juju_cfg.secret)
486 juju_vnf_config = JujuVNFConfig.from_yaml_cfg(yaml_str)
488 rpc_request = AddCorporationRequest.from_yaml_cfg(yaml_str)
489 pe_param_group_map = rpc_request.get_pe_parameter_group_map()
491 pe_name_charm_map = {}
492 for pe_name, pe_group_cfg in pe_param_group_map.items():
493 # The PE name (i.e. PE1) must be in the parameter group name so we can correlate
494 # to an actual VNF in the descriptor.
495 pe_vnf_index = OSM_MWC_Demo.get_pe_vnf_index(pe_name)
497 # Get the deployed VNFR charm service name
498 pe_charm_service_name = juju_vnf_config.get_service_name(pe_vnf_index)
500 pe_name_charm_map[pe_name] = SixWindPEProxyCharm(juju_client, pe_charm_service_name)
502 # At this point we have SixWindPEProxyCharm() instances for each PE and each
503 # PE param group configuration.
504 for src_pe_name in pe_param_group_map:
506 src_pe_name=src_pe_name,
507 src_pe_charm=pe_name_charm_map[src_pe_name],
508 src_pe_group_cfg=PEGroupConfig(pe_param_group_map[src_pe_name]),
509 corporation_name=rpc_request.corporation_name
512 # Create a permutation of all PE's involved in this topology and connect
513 # them together by creating tunnels with matching keys
514 for src_pe_name, dest_pe_name in itertools.permutations(pe_name_charm_map, 2):
516 src_pe_name=src_pe_name,
517 src_pe_charm=pe_name_charm_map[src_pe_name],
518 dest_pe_name=dest_pe_name,
519 corporation_name=rpc_request.corporation_name,
520 tunnel_key=rpc_request.tunnel_key,
523 if __name__ == "__main__":
526 except Exception as e:
527 logger.exception("Caught exception when executing add_corporation ns")