From bd70fc7f0b8fd0ce0a2f2bd46b3c20959d82f63c Mon Sep 17 00:00:00 2001 From: Philip Joseph Date: Mon, 14 Nov 2016 06:57:38 +0000 Subject: [PATCH] New Feature: VCA config parameter request and source Feature name changed from VNF access point Signed-off-by: Philip Joseph --- common/python/rift/mano/config_data/config.py | 4 +- .../mano/config_data/test/test_converter.py | 10 +- examples/ping_pong_ns/CMakeLists.txt | 7 +- .../rift/mano/examples/ping_pong_nsd.py | 389 ++++++++++++- .../rift/mano/examples/ping_rate.py | 113 ++++ .../{ping_config.py => ping_scale.py} | 0 .../rift/mano/examples/ping_setup.py | 143 +++++ .../rift/mano/examples/ping_start_stop.py | 123 +++++ .../rift/mano/examples/pong_setup.py | 144 +++++ .../rift/mano/examples/pong_start_stop.py | 123 +++++ .../rift/mano/examples/start_traffic.py | 49 +- models/plugins/yang/mano-types.yang | 55 +- models/plugins/yang/nsd.yang | 80 +-- models/plugins/yang/vnfd.yang | 172 +++--- .../vala/rwcal_openstack/rwcal_openstack.py | 3 - .../rift/tasklets/rwconmantasklet/RiftCA.py | 205 ++++++- .../tasklets/rwconmantasklet/RiftCM_rpc.py | 2 +- .../rift/tasklets/rwconmantasklet/jujuconf.py | 320 +++++++---- .../rwconmantasklet/riftcm_config_plugin.py | 171 +++++- .../rwconmantasklet/rwconman_conagent.py | 6 +- .../rwconmantasklet/rwconman_config.py | 510 +++++++++++++++--- .../rwconmantasklet/rwconman_events.py | 33 +- .../rwconmantasklet/rwconmantasklet.py | 4 +- rwcm/plugins/yang/rw-conman.yang | 64 +++ .../rift/tasklets/rwmonitor/tasklet.py | 16 +- .../tasklets/rwnsmtasklet/rwnsm_conman.py | 58 +- .../tasklets/rwnsmtasklet/rwnsmtasklet.py | 52 +- .../tasklets/rwvnfmtasklet/rwvnfmtasklet.py | 24 +- rwlaunchpad/test/mano_ut.py | 2 +- 29 files changed, 2473 insertions(+), 409 deletions(-) create mode 100755 examples/ping_pong_ns/rift/mano/examples/ping_rate.py rename examples/ping_pong_ns/rift/mano/examples/{ping_config.py => ping_scale.py} (100%) create mode 100755 examples/ping_pong_ns/rift/mano/examples/ping_setup.py create mode 100755 examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py create mode 100755 examples/ping_pong_ns/rift/mano/examples/pong_setup.py create mode 100755 examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py diff --git a/common/python/rift/mano/config_data/config.py b/common/python/rift/mano/config_data/config.py index 63a2e48d..52109041 100644 --- a/common/python/rift/mano/config_data/config.py +++ b/common/python/rift/mano/config_data/config.py @@ -250,7 +250,7 @@ class ConfigPrimitiveConvertor(object): def extract_vnfd_config(self, vnfd, format="yaml"): config_prim = None try: - config_prim = vnfd.vnf_configuration.service_primitive + config_prim = vnfd.vnf_configuration.config_primitive except AttributeError: pass @@ -339,7 +339,7 @@ class ConfigPrimitiveConvertor(object): vnfd_init_cfg_prim_msg.append(prim) def merge_vnfd_config(self, vnfd, input_data): - for config_primitive in vnfd.vnf_configuration.service_primitive: + for config_primitive in vnfd.vnf_configuration.config_primitive: try: cfg = input_data[self.CONFIG_PRIMITIVE][config_primitive.name] except KeyError: diff --git a/common/python/rift/mano/config_data/test/test_converter.py b/common/python/rift/mano/config_data/test/test_converter.py index 1bfd7d76..4d29b67c 100644 --- a/common/python/rift/mano/config_data/test/test_converter.py +++ b/common/python/rift/mano/config_data/test/test_converter.py @@ -222,14 +222,14 @@ def test_vnfd_config_prim(vnfd, convertor): ], }) - vnf_config.service_primitive.add().from_dict({ + vnf_config.config_primitive.add().from_dict({ "name": "PE1", "parameter": [ {"name": "Foo", "default_value": "Bar"} ] }) - expected_yaml = """service_primitive: + expected_yaml = """config_primitive: PE1: parameter: Foo: Bar @@ -267,12 +267,12 @@ def test_vnfd_merge(vnfd, convertor): "parameter": [{"name": "cidr"}], }) - vnf_config.service_primitive.add().from_dict({ + vnf_config.config_primitive.add().from_dict({ "name": "PE1", "parameter": [{"name": "Foo",}] }) - ip_yaml = """service_primitive: + ip_yaml = """config_primitive: PE1: parameter: Foo: Bar @@ -311,7 +311,7 @@ initial_config_primitive: ], }) - vnf_config.service_primitive.add().from_dict({ + vnf_config.config_primitive.add().from_dict({ "name": "PE1", "parameter": [ {"name": "Foo", "default_value": "Bar"} diff --git a/examples/ping_pong_ns/CMakeLists.txt b/examples/ping_pong_ns/CMakeLists.txt index e373c2a2..2dd5b28c 100644 --- a/examples/ping_pong_ns/CMakeLists.txt +++ b/examples/ping_pong_ns/CMakeLists.txt @@ -67,12 +67,17 @@ rift_python_install_tree( COMPONENT ${PKG_LONG_NAME} FILES rift/mano/examples/ping_pong_nsd.py + rift/mano/examples/ping_setup.py + rift/mano/examples/ping_rate.py + rift/mano/examples/ping_start_stop.py + rift/mano/examples/pong_setup.py + rift/mano/examples/pong_start_stop.py rift/mano/examples/start_traffic.py ) install( PROGRAMS - rift/mano/examples/ping_config.py + rift/mano/examples/ping_scale.py stand_up_ping_pong DESTINATION usr/bin COMPONENT ${PKG_LONG_NAME} diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py index d84a912e..6688c760 100755 --- a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py +++ b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py @@ -125,9 +125,285 @@ class VirtualNetworkFunction(ManoDescriptor): def add_placement_group(self, group): self._placement_groups.append(group) + def add_vnf_access_point(self, mano_ut=False): + vnfd = self.descriptor.vnfd[0] + confparam = vnfd.config_parameter + + def get_params(param): + # Check if pong + if 'pong_' in self.name: + params = [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": param + }, + { + "config_primitive_name_ref": "start-stop", + "config_primitive_parameter_ref": param + }, + ] + else: + params = [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": param + }, + { + "config_primitive_name_ref": "set-rate", + "config_primitive_parameter_ref": param + }, + { + "config_primitive_name_ref": "start-stop", + "config_primitive_parameter_ref": param + }, + ] + return params + + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "mgmt_ip", + "description": "Management address", + "attribute": "../../../../mgmt-interface, ip-address", + "parameter" : get_params("mgmt_ip") + }) + confparam.config_parameter_source.append(src) + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "mgmt_port", + "description": "Management port", + "descriptor": "../../../../mgmt-interface/port", + "parameter" : get_params("mgmt_port") + }) + confparam.config_parameter_source.append(src) + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "username", + "description": "Management username", + "value": "admin", + "parameter" : get_params("username") + }) + confparam.config_parameter_source.append(src) + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "password", + "description": "Management password", + "value": "admin", + "parameter" : get_params("password") + }) + confparam.config_parameter_source.append(src) + + # Check if pong + if 'pong_' in self.name: + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "service_ip", + "description": "IP on which Pong service is listening", + "attribute": "../../../../connection-point[name='pong_vnfd/cp0'], ip-address", + "parameter" : [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": "service_ip" + }, + ] + }) + confparam.config_parameter_source.append(src) + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "service_port", + "description": "Port on which server listens for incoming data packets", + "value": "5555", + "parameter" : [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": "service_port" + }, + ] + }) + confparam.config_parameter_source.append(src) + + else: + src = confparam.create_config_parameter_source() + src.from_dict({ + "name": "rate", + "description": "Rate of packet generation", + "value": "5", + "parameter" : [ + { + "config_primitive_name_ref": "set-rate", + "config_primitive_parameter_ref": "rate" + }, + ] + }) + confparam.config_parameter_source.append(src) + + req = confparam.create_config_parameter_request() + req.from_dict({ + "name": "pong_ip", + "description": "IP on which Pong service is listening", + "parameter" : [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": "pong_ip" + }, + ] + }) + confparam.config_parameter_request.append(req) + req = confparam.create_config_parameter_request() + req.from_dict({ + "name": "pong_port", + "description": "Port on which Pong service is listening", + "parameter" : [ + { + "config_primitive_name_ref": "config", + "config_primitive_parameter_ref": "pong_port" + }, + ] + }) + confparam.config_parameter_request.append(req) + + def add_ping_config(self, mano_ut=False, use_ns_init_conf=False): + vnfd = self.descriptor.vnfd[0] + # Add vnf configuration + vnf_config = vnfd.vnf_configuration + + # vnf_config.config_attributes.config_delay = 10 + + # Select "script" configuration + vnf_config.script.script_type = 'bash' + + # Add config primitive + vnf_config.create_config_primitive() + prim = VnfdYang.ConfigPrimitive.from_dict({ + "name": "config", + "parameter": [ + {"name": "mgmt_ip", "data_type": "STRING"}, + {"name": "mgmt_port", "data_type": "INTEGER"}, + {"name": "username", "data_type": "STRING"}, + {"name": "password", "data_type": "STRING"}, + {"name": "pong_ip", "data_type": "STRING"}, + {"name": "pong_port", "data_type": "INTEGER", + "default_value": "5555"}, + ], + "user_defined_script": "ping_setup.py", + }) + vnf_config.config_primitive.append(prim) + + prim = VnfdYang.ConfigPrimitive.from_dict({ + "name": "set-rate", + "parameter": [ + {"name": "mgmt_ip", "data_type": "STRING"}, + {"name": "mgmt_port", "data_type": "INTEGER"}, + {"name": "username", "data_type": "STRING"}, + {"name": "password", "data_type": "STRING"}, + {"name": "rate", "data_type": "INTEGER", + "default_value": "5"}, + ], + "user_defined_script": "ping_rate.py", + }) + vnf_config.config_primitive.append(prim) + + prim = VnfdYang.ConfigPrimitive.from_dict({ + "name": "start-stop", + "parameter": [ + {"name": "mgmt_ip", "data_type": "STRING"}, + {"name": "mgmt_port", "data_type": "INTEGER"}, + {"name": "username", "data_type": "STRING"}, + {"name": "password", "data_type": "STRING"}, + {"name": "start", "data_type": "BOOLEAN", + "default_value": "true"} + ], + "user_defined_script": "ping_start_stop.py", + }) + vnf_config.config_primitive.append(prim) + + # Add initial config primitive + vnf_config.create_initial_config_primitive() + init_config = VnfdYang.InitialConfigPrimitive.from_dict( + { + "seq": 1, + "config_primitive_ref": "config", + } + ) + vnf_config.initial_config_primitive.append(init_config) + + init_config = VnfdYang.InitialConfigPrimitive.from_dict( + { + "seq": 2, + "config_primitive_ref": "set-rate", + }, + ) + vnf_config.initial_config_primitive.append(init_config) + + if use_ns_init_conf is False: + init_config = VnfdYang.InitialConfigPrimitive.from_dict( + { + "seq": 3, + "config_primitive_ref": "start-stop", + }, + ) + vnf_config.initial_config_primitive.append(init_config) + + def add_pong_config(self, mano_ut=False, use_ns_init_conf=False): + vnfd = self.descriptor.vnfd[0] + # Add vnf configuration + vnf_config = vnfd.vnf_configuration + + # Select "script" configuration + vnf_config.script.script_type = 'bash' + + # Add config primitive + vnf_config.create_config_primitive() + prim = VnfdYang.ConfigPrimitive.from_dict({ + "name": "config", + "parameter": [ + {"name": "mgmt_ip", "data_type": "STRING"}, + {"name": "mgmt_port", "data_type": "INTEGER"}, + {"name": "username", "data_type": "STRING"}, + {"name": "password", "data_type": "STRING"}, + {"name": "service_ip", "data_type": "STRING"}, + {"name": "service_port", "data_type": "INTEGER"}, + ], + "user_defined_script": "pong_setup.py", + }) + vnf_config.config_primitive.append(prim) + + prim = VnfdYang.ConfigPrimitive.from_dict({ + "name": "start-stop", + "parameter": [ + {"name": "mgmt_ip", "data_type": "STRING"}, + {"name": "mgmt_port", "data_type": "INTEGER"}, + {"name": "username", "data_type": "STRING"}, + {"name": "password", "data_type": "STRING"}, + {"name": "start", "data_type": "BOOLEAN", + "default_value": "true"} + ], + "user_defined_script": "pong_start_stop.py", + }) + vnf_config.config_primitive.append(prim) + + # Add initial config primitive + vnf_config.create_initial_config_primitive() + init_config = VnfdYang.InitialConfigPrimitive.from_dict( + { + "seq": 1, + "config_primitive_ref": "config", + } + ) + vnf_config.initial_config_primitive.append(init_config) + + if use_ns_init_conf is False: + init_config = VnfdYang.InitialConfigPrimitive.from_dict( + { + "seq": 2, + "config_primitive_ref": "start-stop", + }, + ) + vnf_config.initial_config_primitive.append(init_config) + def compose(self, image_name, cloud_init="", cloud_init_file="", endpoint=None, mon_params=[], mon_port=8888, mgmt_port=8888, num_vlr_count=1, num_ivlr_count=1, - num_vms=1, image_md5sum=None, mano_ut=False): + num_vms=1, image_md5sum=None, mano_ut=False, use_ns_init_conf=False, + use_vca_conf=False): self.descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog() self.id = str(uuid.uuid1()) vnfd = self.descriptor.vnfd.add() @@ -202,6 +478,16 @@ class VirtualNetworkFunction(ManoDescriptor): if aws: vdu.cloud_init += " - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n" + # Add VNF access point + if use_vca_conf: + self.add_vnf_access_point(mano_ut=mano_ut) + if 'pong_' in self.name: + self.add_pong_config(mano_ut=mano_ut, + use_ns_init_conf=use_ns_init_conf) + else: + self.add_ping_config(mano_ut=mano_ut, + use_ns_init_conf=use_ns_init_conf) + # sepcify the guest EPA if use_epa: vdu.guest_epa.trusted_execution = False @@ -260,7 +546,7 @@ class VirtualNetworkFunction(ManoDescriptor): internal_cp.name = cp_name + "/icp{}".format(i) internal_cp.id = cp_name + "/icp{}".format(i) internal_cp.type_yang = 'VPORT' - ivld_cp = internal_vlds[i].internal_connection_point_ref.add() + ivld_cp = internal_vlds[i].internal_connection_point.add() ivld_cp.id_ref = internal_cp.id internal_interface = vdu.internal_interface.add() @@ -297,16 +583,16 @@ class VirtualNetworkFunction(ManoDescriptor): member_vdu.member_vdu_ref = vdu.id - def write_to_file(self, outdir, output_format): + def write_to_file(self, outdir, output_format, use_vca_conf=False): dirpath = "%s/%s" % (outdir, self.name) if not os.path.exists(dirpath): os.makedirs(dirpath) super(VirtualNetworkFunction, self).write_to_file(['vnfd', 'rw-vnfd'], dirpath, output_format) - self.add_scripts(outdir) + self.add_scripts(outdir, use_vca_conf=use_vca_conf) - def add_scripts(self, outdir): + def add_cloud_init(self, outdir): script_dir = os.path.join(outdir, self.name, 'cloud_init') try: os.makedirs(script_dir) @@ -314,7 +600,7 @@ class VirtualNetworkFunction(ManoDescriptor): if not os.path.isdir(script_dir): raise - if 'ping' in self.name: + if 'ping_' in self.name: script_file = os.path.join(script_dir, 'ping_cloud_init.cfg') cfg = PING_USERDATA_FILE else: @@ -324,6 +610,35 @@ class VirtualNetworkFunction(ManoDescriptor): with open(script_file, "w") as f: f.write("{}".format(cfg)) + def add_scripts(self, outdir, use_vca_conf=False): + self.add_cloud_init(outdir) + if use_vca_conf: + self.add_vca_scripts(outdir) + + def add_vca_scripts(self, outdir): + dest_path = os.path.join(outdir, self.name, 'scripts') + try: + os.makedirs(dest_path) + except OSError: + if not os.path.isdir(dest_path): + raise + + if 'pong_' in self.name: + scripts = ['pong_setup.py', 'pong_start_stop.py'] + else: + scripts = ['ping_setup.py', 'ping_rate.py', 'ping_start_stop.py'] + + for script_name in scripts: + src_path = os.path.dirname(os.path.abspath( + os.path.realpath(__file__))) + script_src = os.path.join(src_path, script_name) + if not os.path.exists(script_src): + src_path = os.path.join(os.environ['RIFT_ROOT'], + 'modules/core/mano/examples/' + 'ping_pong_ns/rift/mano/examples') + script_src = os.path.join(src_path, script_name) + + shutil.copy2(script_src, dest_path) class NetworkService(ManoDescriptor): def __init__(self, name): @@ -499,8 +814,8 @@ exit 0 # Config parameters can be taken from config.yaml and # actions from actions.yaml in the charm # Config to set the home domain - vnf_config.create_service_primitive() - config = VnfdYang.ServicePrimitive.from_dict({ + vnf_config.create_config_primitive() + config = VnfdYang.ConfigPrimitive.from_dict({ "name": "config", "parameter": [ {"name": "home_domain", "data_type": "STRING"}, @@ -509,9 +824,9 @@ exit 0 {"name": "password", "data_type": "STRING"}, ] }) - vnf_config.service_primitive.append(config) + vnf_config.config_primitive.append(config) - config = VnfdYang.ServicePrimitive.from_dict({ + config = VnfdYang.ConfigPrimitive.from_dict({ "name": "create-update-user", # "user-defined-script":"/tmp/test.py", "parameter": [ @@ -519,15 +834,15 @@ exit 0 {"name": "password", "data_type": "STRING", "mandatory": True}, ] }) - vnf_config.service_primitive.append(config) + vnf_config.config_primitive.append(config) - config = VnfdYang.ServicePrimitive.from_dict({ + config = VnfdYang.ConfigPrimitive.from_dict({ "name": "delete-user", "parameter": [ {"name": "number", "data_type": "STRING", "mandatory": True}, ] }) - vnf_config.service_primitive.append(config) + vnf_config.config_primitive.append(config) def default_config(self, const_vnfd, vnfd, mano_ut, use_ns_init_conf): vnf_config = vnfd.vnfd.vnf_configuration @@ -559,7 +874,7 @@ exit 0 if mano_ut: nsd.service_primitive.add().from_dict( { - "name": "ping config", + "name": "ping scale", "user_defined_script": "{}".format(os.path.join( os.environ['RIFT_ROOT'], 'modules/core/mano', @@ -569,8 +884,8 @@ exit 0 else: nsd.service_primitive.add().from_dict( { - "name": "ping config", - "user_defined_script": "ping_config.py" + "name": "ping scale", + "user_defined_script": "ping_scale.py" }) def ns_initial_config(self, nsd): @@ -612,10 +927,26 @@ exit 0 self.nsd.monitoring_param.append(nsd_monp) param_id += 1 + def add_confparam_map(self): + nsd = self.nsd + confparam_map = nsd.config_parameter_map.add() + confparam_map.id = '1' + confparam_map.config_parameter_source.member_vnf_index_ref = 2 + confparam_map.config_parameter_source.config_parameter_source_ref = 'service_ip' + confparam_map.config_parameter_request.member_vnf_index_ref = 1 + confparam_map.config_parameter_request.config_parameter_request_ref = 'pong_ip' + confparam_map = nsd.config_parameter_map.add() + confparam_map.id = '2' + confparam_map.config_parameter_source.member_vnf_index_ref = 2 + confparam_map.config_parameter_source.config_parameter_source_ref = 'service_port' + confparam_map.config_parameter_request.member_vnf_index_ref = 1 + confparam_map.config_parameter_request.config_parameter_request_ref = 'pong_port' - def compose(self, vnfd_list, cpgroup_list, mano_ut, use_ns_init_conf=True): + def compose(self, vnfd_list, cpgroup_list, mano_ut, + use_ns_init_conf=True, + use_vca_conf=False): if mano_ut: # Disable NS initial config primitive @@ -678,8 +1009,9 @@ exit 0 constituent_vnfd.start_by_default = False constituent_vnfd.vnfd_id_ref = vnfd.descriptor.vnfd[0].id - self.default_config(constituent_vnfd, vnfd, mano_ut, - use_ns_init_conf,) + if use_vca_conf is False: + self.default_config(constituent_vnfd, vnfd, mano_ut, + use_ns_init_conf) member_vnf_index += 1 # Enable config primitives if either mano_ut or @@ -718,6 +1050,8 @@ exit 0 member.member_vnf_index_ref = vnfd_index_map[member_vnfd] # self.create_mon_params(vnfd_list) + if use_vca_conf: + self.add_confparam_map() def write_config(self, outdir, vnfds): @@ -837,7 +1171,7 @@ class ScaleGroup(object): def add_config(self): self.config_action['post_scale_out']= {'ns-config-primitive-name-ref': - 'ping config'} + 'ping scale'} class PlacementGroup(object): def __init__(self, name): @@ -892,6 +1226,7 @@ def generate_ping_pong_descriptors(fmt="json", ex_pong_userdata=None, use_placement_group=True, use_ns_init_conf=True, + use_vca_conf=True, ): # List of connection point groups # Each connection point group refers to a virtual link @@ -936,6 +1271,8 @@ def generate_ping_pong_descriptors(fmt="json", num_vms=num_vnf_vms, image_md5sum=ping_md5sum, mano_ut=mano_ut, + use_ns_init_conf=use_ns_init_conf, + use_vca_conf=use_vca_conf, ) pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix)) @@ -976,6 +1313,8 @@ def generate_ping_pong_descriptors(fmt="json", num_vms=num_vnf_vms, image_md5sum=pong_md5sum, mano_ut=mano_ut, + use_ns_init_conf=use_ns_init_conf, + use_vca_conf=use_vca_conf, ) # Initialize the member VNF index @@ -1041,11 +1380,15 @@ def generate_ping_pong_descriptors(fmt="json", nsd_catalog.compose(vnfd_list, cpgroup_list, mano_ut, - use_ns_init_conf=use_ns_init_conf,) + use_ns_init_conf=use_ns_init_conf, + use_vca_conf=use_vca_conf, + ) if write_to_file: - ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt) - pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt) + ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt, + use_vca_conf=use_vca_conf) + pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt, + use_vca_conf=use_vca_conf) nsd_catalog.write_config(out_dir, vnfd_list) nsd_catalog.write_to_file(out_dir, ping_fmt if nsd_fmt is not None else fmt) diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_rate.py b/examples/ping_pong_ns/rift/mano/examples/ping_rate.py new file mode 100755 index 00000000..b3ab07ba --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_rate.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_rate(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"rate\\":{rate}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/rate'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + rate=rate) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + return proc.returncode + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_rate-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-rate') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_rate(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_config.py b/examples/ping_pong_ns/rift/mano/examples/ping_scale.py similarity index 100% rename from examples/ping_pong_ns/rift/mano/examples/ping_config.py rename to examples/ping_pong_ns/rift/mano/examples/ping_scale.py diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_setup.py b/examples/ping_pong_ns/rift/mano/examples/ping_setup.py new file mode 100755 index 00000000..c05e669b --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_setup.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_setup(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + pong_ip = params['pong_ip'] + pong_port = 5555 + if 'pong_port' in params: + pong_port = params['pong_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"ip\\":\\"{pong_ip}\\", \\"port\\":{pong_port}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/server'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + pong_ip=pong_ip, + pong_port=pong_port) + + logger.debug("Executing cmd: %s", cmd) + count = 0 + delay = 5 + max_tries = 12 + rc = 0 + + while True: + count += 1 + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + if proc.returncode == 0: + # Check if response is 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' in resp: + rc = 0 + break + self._log.error("Got error response: {}".format(resp)) + rc = 1 + break + + elif proc.returncode == 7: + # Connection timeout + if count >= max_tries: + self._log.error("Connect failed for {}. Failing".format(count)) + rc = 7 + break + # Try after delay + time.sleep(delay) + + return rc + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_setup-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-setup') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_setup(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py b/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py new file mode 100755 index 00000000..76f653c0 --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_start_stop(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + start = 'true' + if 'start' in params: + if not params['start']: + start = 'false' + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"enable\\":{start}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/adminstatus/state'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + start=start) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + rc = proc.returncode + + if rc == 0: + # Check if we got 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' not in resp: + self._log.error("Got error response: {}".format(resp)) + rc = 1 + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_start_stop-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-start-stop') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_start_stop(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/pong_setup.py b/examples/ping_pong_ns/rift/mano/examples/pong_setup.py new file mode 100755 index 00000000..cd56eca8 --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/pong_setup.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def pong_setup(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18889 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + service_ip = params['service_ip'] + service_port = 5555 + if 'service_port' in params: + service_port = params['service_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + config_cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"ip\\":\\"{service_ip}\\", \\"port\\":{service_port}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/pong/server'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + service_ip=service_ip, + service_port=service_port) + + logger.debug("Executing cmd: %s", config_cmd) + count = 0 + delay = 5 + max_tries = 12 + rc = 0 + + while True: + count += 1 + proc = subprocess.run(config_cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + if proc.returncode == 0: + # Check if response is 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' in resp: + rc = 0 + break + self._log.error("Got error response: {}".format(resp)) + rc = 1 + break + + elif proc.returncode == 7: + # Connection timeout + if count >= max_tries: + self._log.error("Connect failed for {}. Failing".format(count)) + rc = 7 + break + # Try after delay + time.sleep(delay) + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/pong_setup-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('pong-setup') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = pong_setup(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py b/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py new file mode 100755 index 00000000..b5195ddf --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def pong_start_stop(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18889 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + start = 'true' + if 'start' in params: + if not params['start']: + start = 'false' + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"enable\\":{start}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/pong/adminstatus/state'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + start=start) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + rc = proc.returncode + + if rc == 0: + # Check if we got 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' not in resp: + self._log.error("Got error response: {}".format(resp)) + rc = 1 + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/pong_start_stop-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('pong-start-stop') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = pong_start_stop(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/start_traffic.py b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py index af6f62f6..3093792f 100755 --- a/examples/ping_pong_ns/rift/mano/examples/start_traffic.py +++ b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py @@ -41,7 +41,13 @@ def start_traffic(yaml_cfg, logger): vnf_type=vnf_type) logger.debug("Executing cmd: %s", curl_cmd) - subprocess.check_call(curl_cmd, shell=True) + proc = subprocess.run(curl_cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + return proc.returncode # Enable pong service first for index, vnfr in yaml_cfg['vnfr'].items(): @@ -51,11 +57,15 @@ def start_traffic(yaml_cfg, logger): if 'pong_vnfd' in vnfr['name']: vnf_type = 'pong' port = 18889 - enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + rc = enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + if rc != 0: + logger.error("Enable service for pong failed: {}". + format(rc)) + return rc break # Add a delay to provide pong port to come up - time.sleep(0.1) + time.sleep(1) # Enable ping service next for index, vnfr in yaml_cfg['vnfr'].items(): @@ -65,9 +75,12 @@ def start_traffic(yaml_cfg, logger): if 'ping_vnfd' in vnfr['name']: vnf_type = 'ping' port = 18888 - enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + rc = enable_service(vnfr['mgmt_ip_address'], port, vnf_type) break + return rc + + def main(argv=sys.argv[1:]): try: parser = argparse.ArgumentParser() @@ -79,14 +92,14 @@ def main(argv=sys.argv[1:]): if not os.path.exists(run_dir): os.makedirs(run_dir) log_file = "{}/ping_pong_start_traffic-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) - logging.basicConfig(filename=log_file, level=logging.DEBUG) - logger = logging.getLogger() - except Exception as e: - print("Exception in {}: {}".format(__file__, e)) - sys.exit(1) + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-pong-start-traffic') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) - try: ch = logging.StreamHandler() if args.verbose: ch.setLevel(logging.DEBUG) @@ -95,24 +108,28 @@ def main(argv=sys.argv[1:]): # create formatter and add it to the handlers formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) ch.setFormatter(formatter) + logger.addHandler(fh) logger.addHandler(ch) except Exception as e: - logger.exception(e) - raise e + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) yaml_str = args.yaml_cfg_file.read() - # logger.debug("Input YAML file:\n{}".format(yaml_str)) yaml_cfg = yaml.load(yaml_str) logger.debug("Input YAML: {}".format(yaml_cfg)) - start_traffic(yaml_cfg, logger) + rc = start_traffic(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) except Exception as e: - logger.exception(e) - raise e + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) if __name__ == "__main__": main() diff --git a/models/plugins/yang/mano-types.yang b/models/plugins/yang/mano-types.yang index 46cad20a..cfe58e47 100644 --- a/models/plugins/yang/mano-types.yang +++ b/models/plugins/yang/mano-types.yang @@ -108,6 +108,7 @@ module mano-types "The value should be greyed out by the UI. Only applies to parameters with default values."; type boolean; + default false; } leaf hidden { @@ -115,6 +116,13 @@ module mano-types "The value should be hidden by the UI. Only applies to parameters with default values."; type boolean; + default false; + } + + leaf out { + description "If this is an output of the primitive execution"; + type boolean; + default false; } leaf parameter-type { description "Type of this parameter, whether this in IN or OUT"; @@ -281,25 +289,32 @@ module mano-types } } - list service-primitive { - rwpb:msg-new ServicePrimitive; + list config-primitive { + rwpb:msg-new ConfigPrimitive; description - "List of service primitives supported by the + "List of config primitives supported by the configuration agent for this VNF."; key "name"; leaf name { description - "Name of the service primitive."; + "Name of the config primitive."; type string; } list parameter { description - "List of parameters to the service primitive."; + "List of parameters to the config primitive."; key "name"; uses primitive-parameter; } + + leaf user-defined-script { + description + "A user defined script. If user defined script is defined, + the script will be executed using bash"; + type string; + } } list initial-config-primitive { @@ -313,20 +328,28 @@ module mano-types type uint64; } - leaf name { - description - "Name of the configuration primitive."; - type string; - } + choice primtive-type { + case primtive-definition { + leaf name { + description + "Name of the configuration primitive."; + type string; + } - list parameter { - key "name"; - leaf name { - type string; + uses primitive-parameter-value; } - leaf value { - type string; + case primitive-ref { + leaf config-primitive-ref { + description + "Reference to a config primitive name. + NOTE: The config primitive referred should have + all the input paramaters predefined either + with default values or dependency references."; + type leafref { + path "../../config-primitive/name"; + } + } } } } diff --git a/models/plugins/yang/nsd.yang b/models/plugins/yang/nsd.yang index 467be6d1..a8c97d49 100644 --- a/models/plugins/yang/nsd.yang +++ b/models/plugins/yang/nsd.yang @@ -317,46 +317,6 @@ module nsd } } - list vnfap-map { - key "id"; - description "A mapping of VNF application point - capability/dependency within this network service"; - leaf id { - description "Identfier for VNF access point map"; - type string; - } - container capability { - leaf member-vnf-index-ref { - description "Reference to member-vnf within constituent-vnfds"; - type leafref { - path "../../../../constituent-vnfd/member-vnf-index"; - } - } - leaf capability-ref { - description "Reference to the capability with the capability of the VNF - with the specified member-vnf-index"; - type leafref { - path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:vnfap/vnfd:capability/vnfd:name"; - } - } - } - container dependency { - leaf member-vnf-index-ref { - description "Reference to member-vnf within constituent-vnfds"; - type leafref { - path "../../../../constituent-vnfd/member-vnf-index"; - } - } - leaf dependency-ref { - description "Reference to the dependency within the dependency of the VNF - with the specified member-vnf-index"; - type leafref { - path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:vnfap/vnfd:dependency/vnfd:name"; - } - } - } - } - // replicate for pnfd container here uses manotypes:provider-network; @@ -419,6 +379,46 @@ module nsd } } + list config-parameter-map { + key "id"; + description "A mapping of VNF config parameter + requests and sources within this network service"; + leaf id { + description "Identfier for VNF map"; + type string; + } + container config-parameter-request { + leaf member-vnf-index-ref { + description "Reference to member-vnf within constituent-vnfds"; + type leafref { + path "../../../constituent-vnfd/member-vnf-index"; + } + } + leaf config-parameter-request-ref { + description "Reference to the request in the VNF + with the specified member-vnf-index"; + type leafref { + path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:config-parameter/vnfd:config-parameter-request/vnfd:name"; + } + } + } + container config-parameter-source { + leaf member-vnf-index-ref { + description "Reference to member-vnf within constituent-vnfds"; + type leafref { + path "../../../constituent-vnfd/member-vnf-index"; + } + } + leaf config-parameter-source-ref { + description "Reference to the source in the VNF + with the specified member-vnf-index"; + type leafref { + path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:config-parameter/vnfd:config-parameter-source/vnfd:name"; + } + } + } + } + list scaling-group-descriptor { description "scaling group descriptor within this network service. diff --git a/models/plugins/yang/vnfd.yang b/models/plugins/yang/vnfd.yang index 15383410..1c36433e 100644 --- a/models/plugins/yang/vnfd.yang +++ b/models/plugins/yang/vnfd.yang @@ -115,97 +115,137 @@ module vnfd } } - grouping vnf-access-point { - list vnfap { - description "List of VNF access points"; - key "name"; - leaf name { - description "Name of the vnf access point"; - type string { - length 128; - } - } - leaf description { - description "A description of this VNF access point"; - type string; - } - list capability { - description "The List of capabilities of this VNF access point"; + grouping config-parameter { + container config-parameter { + rwpb:msg-new ConfigParams; + description + "List of VNF config parameter requests and sources"; + list config-parameter-source { + description "The list of parameters exposed by this VNF"; key "name"; + leaf name { - description "Name of the capability"; + description "Name of the source"; type string { - length 128; + length "1..128"; } } + leaf description { - description "A description of the capability"; + description " Description of the source"; type string; } - choice capability-location { - case location-path { - leaf xpath { - description "Location of this capaibility as an xpath. - This field points to an element within vnfd - with the xpath notation and additionally - provides the flexibility to choose attributes - within the instantiated version of this object. - For example, - /vnfd-catalog/vnfd[id='xyz']/connection-point[name='cp1']/ip_address() - refers to the ip address of the connection point with name 'cp1' - in VNFD with id 'xyz'."; + + choice source { + case descriptor { + leaf descriptor { + description + "Location of this source as an xpath. + For example: + ../../../mgmt-interface/port"; type string; } } - case location-primitive-ref { - leaf config-primitive-name-ref { - description "A leafref to configuration primitive. - This field contains a link to the config primitive - the output paramaters of which will satisfy this requirement."; - type leafref { - path "/vnfd:vnfd-catalog/vnfd/vnf-configuration/service-primitive/name"; - } + + case attirbute { + leaf attribute { + description + "Location of this source as runtime attribute. + The value is , + For example: + ../../../mgmt-interface, ip-address + which retruns the ip-address assigned to the + mgmt-interface after VNF instantiation."; + type string; } } - case user-input { - list user-input { - key "name"; - description "User input for this capability"; - leaf name { - description "Name of this input"; - type string; + + case primitive-ref { + leaf config-primitive-name-ref { + description + "A leafref to configuration primitive. + This refers to a config parameter whose + output parameter is referred in out-parameter."; + type leafref { + path "../../../vnf-configuration/config-primitive/name"; } - leaf value { - description "Value of this input"; - type string; + } + + leaf parameter-ref { + description + "Name of the output parameter in the config primitiive"; + type leafref { + path + "../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name"; } } } + + case value { + leaf value { + description + "Pre-defined value to be used for this source"; + type string; + } + } + } + + list parameter { + key "config-primitive-name-ref"; + + leaf config-primitive-name-ref { + description + "Name of the configuration primitive where this + request will used"; + type leafref { + path "../../../../vnf-configuration/config-primitive/name"; + } + } + + leaf config-primitive-parameter-ref { + description + "Parameter name of the config primitive"; + type leafref { + path "../../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name"; + } + } } } - list dependency { - description "The List of depenencies of this VNF access point"; + + list config-parameter-request { + description "The list of requests for this VNF"; key "name"; + leaf name { - description "Name of this VNF access point"; + description "Name of this parameter request"; type string { - length 128; + length "1..128"; } } - leaf mandatory { - description "Is this dependcy mandatory for this dependency"; - type boolean; - } + leaf description { - description "A description of the dependency"; + description "Description of this request"; type string; } - leaf config-primitive-name-ref { - description "A leafref to configuration primitive. - This field contains a link to the config primitive - the input paramaters of which will satisfy this requirement."; - type leafref { - path "/vnfd:vnfd-catalog/vnfd/vnf-configuration/service-primitive/name"; + + list parameter { + key "config-primitive-name-ref"; + + leaf config-primitive-name-ref { + description + "Name of the configuration primitive where this + request will used"; + type leafref { + path "../../../../vnf-configuration/config-primitive/name"; + } + } + + leaf config-primitive-parameter-ref { + description + "Parameter name of the config primitive"; + type leafref { + path "../../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name"; + } } } } @@ -252,6 +292,8 @@ module vnfd uses manotypes:vnf-configuration; + uses config-parameter; + container mgmt-interface { description "Interface over which the VNF is managed."; @@ -606,8 +648,6 @@ module vnfd key "id"; uses vnfd-descriptor; - - uses vnf-access-point; } } } diff --git a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py index 8631ae2e..8a4c3374 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py +++ b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py @@ -29,13 +29,10 @@ import rift.cal.rwcal_status as rwcal_status import rwlogger import neutronclient.common.exceptions as NeutronException import keystoneclient.exceptions as KeystoneExceptions -<<<<<<< 06b6ab4edf863c75fc6e2947ad52d1c76de1a308 import tornado import gi gi.require_version('RwSdn', '1.0') -======= ->>>>>>> RIFT-14308 Meaningful logs while initalizing Openstack driver from gi.repository import ( GObject, diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py index 0400a54a..7309d2e5 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py @@ -16,10 +16,11 @@ import asyncio import concurrent.futures +import os import re +import shlex import tempfile import yaml -import os from gi.repository import ( RwDts as rwdts, @@ -94,7 +95,7 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): def riftca_log(self, name, level, log_str, *args): getattr(self._log, level)('RiftCA:({}) {}'.format(name, log_str), *args) - + @asyncio.coroutine def notify_create_vnfr(self, agent_nsr, agent_vnfr): """ @@ -140,12 +141,134 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): pass @asyncio.coroutine - def vnf_config_primitive(self, agent_nsr, agent_vnfr, primitive, output): + def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive, + vnf_config=None): + ''' + Pass vnf_config to avoid querying DTS each time + ''' + self._log.debug("VNF config primitive {} for nsr {}, vnfr {}". + format(primitive.name, nsr_id, vnfr_id)) + + if vnf_config is None: + vnfr = yield from self.get_vnfr(vnfr_id) + if vnfr is None: + self._log.error("Unable to get VNFR {} through DTS". + format(vnfr_id)) + return 1, "Unable to get VNFR {} through DTS".format(vnfr_id) + + vnf_config = vnfr.vnf_configuration + self._log.debug("VNF config= %s", vnf_config.as_dict()) + + data = {} + script = None + found = False + + configs = vnf_config.config_primitive + for config in configs: + if config.name == primitive.name: + found = True + self._log.debug("RiftCA: Found the config primitive %s", + config.name) + + spt = config.user_defined_script + if spt is None: + self._log.error("RiftCA: VNFR {}, Did not find " + "script defined in config {}". + format(vnfr['name'], config.as_dict())) + return 1, "Did not find user defined script for " \ + "config primitive {}".format(primitive.name) + + spt = shlex.quote(spt.strip()) + if spt[0] == '/': + script = spt + else: + script = os.path.join(self._rift_artif_dir, + 'launchpad/libs', + nsr_id, + 'scripts', + spt) + self._log.debug("Rift config agent: Checking for script " + "in %s", script) + if not os.path.exists(script): + self._log.debug("Rift config agent: Did not find " + "script %s", script) + script = os.path.join(self._rift_install_dir, + 'usr/bin', spt) + if not os.path.exists(script): + self._log.debug("Rift config agent: Did not find " + "script %s", script) + return 1, "Did not find user defined " \ + "script {}".format(spt) + + params = {} + for param in config.parameter: + val = None + for p in primitive.parameter: + if p.name == param.name: + val = p.value + break + + if val is None: + val = param.default_value + + if val is None: + # Check if mandatory parameter + if param.mandatory: + msg = "VNFR {}: Primitive {} called " \ + "without mandatory parameter {}". \ + format(vnfr.name, config.name, + param.name) + self._log.error(msg) + return 1, msg + + if val: + val = self.convert_value(val, param.data_type) + params.update({param.name: val}) + + data['parameters'] = params + break + + if not found: + msg = "Did not find the primitive {} in VNFR {}". \ + format(primitive.name, vnfr.name) + self._log.error(msg) + return 1, msg + + rc, script_err = yield from self.exec_script(script, data) + return rc, script_err + + @asyncio.coroutine + def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): ''' primitives support by RiftCA + + Pass vnf_config to avoid querying DTS each time ''' - pass - + output.execution_status = "failed" + output.execution_id = '' + output.execution_error_details = '' + + try: + vnfr = self._rift_vnfs[vnfr_id].vnfr + except KeyError: + msg = "Did not find VNFR %s in RiftCA plugin", vnfr_id + self._log.error(msg) + output.execution_error_details = msg + return + + rc, err = yield from self._vnf_config_primitive(nsr_id, + vnfr_id, + primitive) + self._log.debug("VNFR {} primitive {} exec status: {}". + format(vnfr.name, primitive.name, rc)) + + if rc == 0: + output.execution_status = "completed" + else: + if isinstance(err, bytes): + err = err.decode() + output.execution_error_details = err + @asyncio.coroutine def apply_config(self, config, nsr, vnfr, rpc_ip): """ Notification on configuration of an NSR """ @@ -265,6 +388,67 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): return task, err + @asyncio.coroutine + def apply_initial_config_new(self, agent_nsr, agent_vnfr): + self._log.debug("RiftCA: VNF initial config primitive for nsr {}, vnfr {}". + format(agent_nsr.name, agent_vnfr.name)) + + try: + vnfr = self._rift_vnfs[agent_vnfr.id].vnfr + except KeyError: + self._log.error("RiftCA: Did not find VNFR %s in RiftCA plugin", + agent_vnfr.name) + return False + + class Primitive: + def __init__(self, name): + self.name = name + self.value = None + self.parameter = [] + + vnfr = yield from self.get_vnfr(agent_vnfr.id) + if vnfr is None: + msg = "Unable to get VNFR {} ({}) through DTS". \ + format(agent_vnfr.id, agent_vnfr.name) + self._log.error(msg) + raise RuntimeError(msg) + + vnf_config = vnfr.vnf_configuration + self._log.debug("VNFR %s config: %s", vnfr.name, + vnf_config.as_dict()) + + # Sort the primitive based on the sequence number + primitives = sorted(vnf_config.initial_config_primitive, + key=lambda k: k.seq) + if not primitives: + self._log.debug("VNFR {}: No initial-config-primitive specified". + format(vnfr.name)) + return True + + for primitive in primitives: + if primitive.config_primitive_ref: + # Reference to a primitive in config primitive + prim = Primitive(primitive.config_primitive_ref) + rc, err = yield from self._vnf_config_primitive(agent_nsr.id, + agent_vnfr.id, + prim, + vnf_config) + if rc != 0: + msg = "Error executing initial config primitive" \ + " {} in VNFR {}: rc={}, stderr={}". \ + format(prim.name, vnfr.name, rc, err) + self._log.error(msg) + return False + + elif primitive.name: + msg = "Primitive {} definition in initial config " \ + "primitive for VNFR {} not supported yet". \ + format(primtive.name, vnfr.name) + self._log.error(msg) + raise NotImplementedError(msg) + + return True + @asyncio.coroutine def apply_initial_config(self, agent_nsr, agent_vnfr): """ @@ -280,16 +464,23 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): agent_vnfr.set_to_configurable() if agent_vnfr.is_configurable: # apply initial config for the vnfr + # Keep the config-template based initial-config rc = yield from self._events.apply_vnf_config(agent_vnfr.vnf_cfg) + + # Check if the new method of initial-config-primitive is present + if rc: + rc = yield from self.apply_initial_config_new(agent_nsr, agent_vnfr) + else: self._log.info("Rift config agent: VNF:%s/%s is not configurable yet!", agent_nsr.name, agent_vnfr.name) + except Exception as e: self._log.error("Rift config agent: Error on initial configuration to VNF:{}/{}, e {}" .format(agent_nsr.name, agent_vnfr.name, str(e))) - + self._log.exception(e) - return rc + return False return rc diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py index 5f330249..a215f67e 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py @@ -105,7 +105,7 @@ class RiftCMRPCHandler(object): if vnf: self._log.debug("nsr/vnf {}/{}, vnf_configuration: %s", vnf.vnf_configuration) - for primitive in vnf.vnf_configuration.service_primitive: + for primitive in vnf.vnf_configuration.config_primitive: if primitive.name == primitive_name: return primitive diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py index 7779479d..73a00051 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py @@ -298,99 +298,153 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): return True @asyncio.coroutine - def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): - self._log.debug("jujuCA: VNF config primititve {} for nsr {}, vnfr_id {}". + def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive, + vnf_config=None): + self._log.debug("jujuCA: VNF config primitive {} for nsr {}, " + "vnfr_id {}". format(primitive, nsr_id, vnfr_id)) + try: vnfr = self._juju_vnfs[vnfr_id].vnfr except KeyError: - self._log.error("jujuCA: Did not find VNFR %s in juju plugin", - vnfr_id) + msg = "Did not find VNFR %s in Juju plugin", vnfr_id + self._log.error(msg) + output.execution_error_details = msg return - output.execution_status = "failed" - output.execution_id = '' - output.execution_error_details = '' + if vnf_config is None: + vnfr_msg = yield from self.get_vnfr(vnfr_id) + if vnfr is None: + self._log.error("Unable to get VNFR {} through DTS". + format(vnfr_id)) + return 'failed', '', "Unable to get VNFR {} through DTS". \ + format(vnfr_id) + + vnf_config = vnfr_msg.vnf_configuration + self._log.debug("VNF config= %s", vnf_config.as_dict()) try: service = vnfr['vnf_juju_name'] - vnf_config = vnfr['config'] self._log.debug("VNF config %s", vnf_config) - configs = vnf_config.service_primitive + configs = vnf_config.config_primitive for config in configs: if config.name == primitive.name: self._log.debug("jujuCA: Found the config primitive %s", config.name) params = {} - for parameter in primitive.parameter: - if parameter.value: - val = self.xlate(parameter.value, vnfr['tags']) - # TBD do validation of the parameters - data_type = 'string' - found = False - for ca_param in config.parameter: - if ca_param.name == parameter.name: - data_type = ca_param.data_type - found = True - break - if data_type == 'integer': - val = int(parameter.value) - if not found: - self._log.warn("jujuCA: Did not find parameter {} for {}". - format(parameter, config.name)) + for parameter in config.parameter: + val = None + for p in primitive.parameter: + if p.name == parameter.name: + if p.value: + val = self.xlate(p.value, vnfr['tags']) + break + + if val is None: + val = parameter.default_value + + if val is None: + # Check if mandatory parameter + if parameter.mandatory: + msg = "VNFR {}: Primitive {} called " \ + "without mandatory parameter {}". \ + format(vnfr_msg.name, config.name, + parameter.name) + self._log.error(msg) + return 'failed', '', msg + + if val: + val = self.convert_value(val, parameter.data_type) params.update({parameter.name: val}) + rc = '' + exec_id = '' + details = '' if config.name == 'config': - output.execution_id = 'config' + exec_id = 'config' if len(params): - self._log.debug("jujuCA: applying config with params {} for service {}". + self._log.debug("jujuCA: applying config with " + "params {} for service {}". format(params, service)) - rc = yield from self.api.apply_config(params, service=service, wait=False) + rc = yield from self.api.apply_config( + params, + service=service, + wait=False) if rc: # Mark as pending and check later for the status - output.execution_status = "pending" - self._log.debug("jujuCA: applied config {} on {}". - format(params, service)) + rc = "pending" + self._log.debug("jujuCA: applied config {} " + "on {}".format(params, service)) else: - output.execution_status = 'failed' - output.execution_error_details = \ + rc = 'failed' + details = \ 'Failed to apply config: {}'.format(params) - self._log.error("jujuCA: Error applying config {} on service {}". + self._log.error("jujuCA: Error applying " + "config {} on service {}". format(params, service)) else: - self._log.warn("jujuCA: Did not find valid parameters for config : {}". + self._log.warn("jujuCA: Did not find valid " + "parameters for config : {}". format(primitive.parameter)) - output.execution_status = "completed" + rc = "completed" else: - self._log.debug("jujuCA: Execute action {} on service {} with params {}". + self._log.debug("jujuCA: Execute action {} on " + "service {} with params {}". format(config.name, service, params)) - resp = yield from self.api.execute_action(config.name, - params, - service=service) + resp = yield from self.api.execute_action( + config.name, + params, + service=service) if resp: if 'error' in resp: - output.execution_error_details = resp['error']['Message'] + details = resp['error']['Message'] else: - output.execution_id = resp['action']['tag'] - output.execution_status = resp['status'] - if output.execution_status == 'failed': - output.execution_error_details = resp['message'] - self._log.debug("jujuCA: execute action {} on service {} returned {}". - format(config.name, service, output.execution_status)) + exec_id = resp['action']['tag'] + rc = resp['status'] + if rc == 'failed': + details = resp['message'] + + self._log.debug("jujuCA: execute action {} on " + "service {} returned {}". + format(config.name, service, rc)) else: - self._log.error("jujuCA: error executing action {} for {} with {}". - format(config.name, service, params)) - output.execution_id = '' - output.execution_status = 'failed' - output.execution_error_details = "Failed to queue the action" + self._log.error("jujuCA: error executing action " + "{} for {} with {}". + format(config.name, service, + params)) + exec_id = '' + rc = 'failed' + details = "Failed to queue the action" break except KeyError as e: - self._log.info("VNF %s does not have config primititves, e=%s", vnfr_id, e) + msg = "VNF %s does not have config primitives, e=%s", \ + vnfr_msg.name, e + self._log.error(msg) + raise ValueError(msg) + + return rc, exec_id, details + + @asyncio.coroutine + def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): + output.execution_status = "failed" + output.execution_id = '' + output.execution_error_details = '' + + rc, exec_id, err = yield from self._vnf_config_primitive( + nsr_id, + vnfr_id, + primitive) + + self._log.debug("VNFR {} primitive {} exec status: {}". + format(vnfr.name, primitive.name, rc)) + output.execution_status = rc + output.execution_id = exec_id + output.execution_error_details = err @asyncio.coroutine def apply_config(self, agent_nsr, agent_vnfr, config, rpc_ip): @@ -497,76 +551,126 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): Actions in initial config may not work based on charm design """ - vnfr = agent_vnfr.vnfr - service = vnfr['vnf_juju_name'] + try: + vnfr = self._juju_vnfs[agent_vnfr.id].vnfr + except KeyError: + self._log.error("Did not find VNFR %s in Juju plugin", + agent_vnfr.name) + return False + + vnfr_msg = yield from self.get_vnfr(agent_vnfr.id) + if vnfr_msg is None: + msg = "Unable to get VNFR {} ({}) through DTS". \ + format(agent_vnfr.id, agent_vnfr.name) + self._log.error(msg) + raise RuntimeError(msg) + + vnf_config = vnfr_msg.vnf_configuration + self._log.debug("VNFR %s config: %s", vnfr_msg.name, + vnf_config.as_dict()) + + # Sort the primitive based on the sequence number + primitives = sorted(vnf_config.initial_config_primitive, + key=lambda k: k.seq) + if not primitives: + self._log.debug("VNFR {}: No initial-config-primitive specified". + format(vnfr_msg.name)) + return True + service = vnfr['vnf_juju_name'] rc = yield from self.api.is_service_up(service=service) if not rc: return False action_ids = [] try: - vnf_cat = agent_vnfr.vnfr_msg - if vnf_cat and vnf_cat.mgmt_interface.ip_address: - vnfr['tags'].update({'rw_mgmt_ip': vnf_cat.mgmt_interface.ip_address}) + if vnfr_msg.mgmt_interface.ip_address: + vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address}) self._log.debug("jujuCA:(%s) tags: %s", vnfr['vnf_juju_name'], vnfr['tags']) - config = {} - try: - for primitive in vnfr['config'].initial_config_primitive: - self._log.debug("jujuCA:(%s) Initial config primitive %s", vnfr['vnf_juju_name'], primitive) + for primitive in primitives: + self._log.debug("(%s) Initial config primitive %s", + vnfr['vnf_juju_name'], primitive) + if primitive.config_primitive_ref: + # Reference to a primitive in config primitive + class Primitive: + def __init__(self, name): + self.name = name + self.value = None + self.parameter = [] + + prim = Primitive(primitive.config_primitive_ref) + rc, eid, err = yield from self._vnf_config_primitive( + agent_nsr.id, + agent_vnfr.id, + prim, + vnf_config) + + if rc == "failed": + msg = "Error executing initial config primitive" \ + " {} in VNFR {}: rc={}, stderr={}". \ + format(prim.name, vnfr_msg.name, rc, err) + self._log.error(msg) + return False + + elif rc == "pending": + action_ids.append(eid) + + elif primitive.name: + config = {} if primitive.name == 'config': for param in primitive.parameter: if vnfr['tags']: - val = self.xlate(param.value, vnfr['tags']) + val = self.xlate(param.value, + vnfr['tags']) config.update({param.name: val}) - except KeyError as e: - self._log.exception("jujuCA:(%s) Initial config error(%s): config=%s", - vnfr['vnf_juju_name'], str(e), config) - config = None - return False - if config: - self.juju_log('info', vnfr['vnf_juju_name'], - "Applying Initial config:%s", - config) + if config: + self.juju_log('info', vnfr['vnf_juju_name'], + "Applying Initial config:%s", + config) - rc = yield from self.api.apply_config(config, service=service) - if rc is False: - self.log.error("Service {} is in error state".format(service)) - return False - - - # Apply any actions specified as part of initial config - for primitive in vnfr['config'].initial_config_primitive: - if primitive.name != 'config': - self._log.debug("jujuCA:(%s) Initial config action primitive %s", - vnfr['vnf_juju_name'], primitive) - action = primitive.name - params = {} - for param in primitive.parameter: - val = self.xlate(param.value, vnfr['tags']) - params.update({param.name: val}) - - self._log.info("jujuCA:(%s) Action %s with params %s", - vnfr['vnf_juju_name'], action, params) - - resp = yield from self.api.execute_action(action, params, - service=service) - if 'error' in resp: - self._log.error("Applying initial config on {} failed for {} with {}: {}". - format(vnfr['vnf_juju_name'], action, params, resp)) - return False + rc = yield from self.api.apply_config( + config, + service=service) + if rc is False: + self.log.error("Service {} is in error state". + format(service)) + return False - action_ids.append(resp['action']['tag']) + # Apply any actions specified as part of initial config + else: + self._log.debug("(%s) Initial config action " + "primitive %s", + vnfr['vnf_juju_name'], primitive) + action = primitive.name + params = {} + for param in primitive.parameter: + val = self.xlate(param.value, vnfr['tags']) + params.update({param.name: val}) + + self._log.info("(%s) Action %s with params %s", + vnfr['vnf_juju_name'], action, + params) + + resp = yield from self.api.execute_action( + action, + params, + service=service) + if 'error' in resp: + self._log.error("Applying initial config on {}" + " failed for {} with {}: {}". + format(vnfr['vnf_juju_name'], + action, params, resp)) + return False + + action_ids.append(resp['action']['tag']) - except KeyError as e: - self._log.info("Juju config agent(%s): VNFR %s not managed by Juju", - vnfr['vnf_juju_name'], agent_vnfr.id) - return False except Exception as e: - self._log.exception("jujuCA:(%s) Exception juju apply_initial_config for VNFR {}: {}". - format(vnfr['vnf_juju_name'], agent_vnfr.id, e)) + self._log.exception("jujuCA:(%s) Exception juju " + "apply_initial_config for VNFR {}: {}". + format(vnfr['vnf_juju_name'], + agent_vnfr.id, e)) return False # Check if all actions completed @@ -576,11 +680,13 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): for act in action_ids: resp = yield from self.api.get_action_status(act) if 'error' in resp: - self._log.error("Initial config failed: {}".format(resp)) + self._log.error("Initial config failed for action {}: {}". + format(act, resp)) return False if resp['status'] == 'failed': - self._log.error("Initial config action failed: {}".format(resp)) + self._log.error("Initial config action failed for " + "action {}: {}".format(act, resp)) return False if resp['status'] == 'pending': diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py index f285afd7..78f0aa0d 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py @@ -16,10 +16,41 @@ import asyncio import abc +import os +import tempfile +import yaml + +import gi +gi.require_version('RwDts', '1.0') +from gi.repository import ( + RwDts as rwdts, +) # Default config agent plugin type DEFAULT_CAP_TYPE = "riftca" + +class XPaths(object): + @staticmethod + def nsr_opdata(k=None): + return ("D,/nsr:ns-instance-opdata/nsr:nsr" + + ("[nsr:ns-instance-config-ref='{}']".format(k) if k is not None else "")) + + @staticmethod + def nsd_msg(k=None): + return ("C,/nsd:nsd-catalog/nsd:nsd" + + "[nsd:id = '{}']".format(k) if k is not None else "") + + @staticmethod + def vnfr_opdata(k=None): + return ("D,/vnfr:vnfr-catalog/vnfr:vnfr" + + ("[vnfr:id='{}']".format(k) if k is not None else "")) + + @staticmethod + def nsr_config(k=None): + return ("C,/nsr:ns-instance-config/nsr:nsr[nsr:id='{}']".format(k) if k is not None else "") + + class RiftCMnsr(object): ''' Agent class for NSR @@ -57,6 +88,10 @@ class RiftCMnsr(object): def nsr_cfg_msg(self): return self._cfg + @property + def nsd(self): + return self._cfg.nsd + @property def job_id(self): ''' Get a new job id for config primitive''' @@ -71,11 +106,11 @@ class RiftCMnsr(object): def member_vnf_index(self): return self._vnfr['member_vnf_index_ref'] - def add_vnfr(self, vnfr, vnfr_msg): + def add_vnfr(self, vnfr, vnfr_msg, vnfd): if vnfr['id'] in self._vnfr_ids.keys(): agent_vnfr = self._vnfr_ids[vnfr['id']] else: - agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg) + agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg, vnfd) self._vnfrs.append(agent_vnfr) self._vnfrs_msg.append(vnfr_msg) self._vnfr_ids[agent_vnfr.id] = agent_vnfr @@ -85,13 +120,20 @@ class RiftCMnsr(object): def vnfr_ids(self): return self._vnfr_ids + def get_member_vnfr(self, member_index): + for vnfr in self._vnfrs: + if vnfr.member_vnf_index == member_index: + return vnfr + + class RiftCMvnfr(object): ''' Agent base class for VNFR processing ''' - def __init__(self, nsr_name, vnfr_dict, vnfr_msg): + def __init__(self, nsr_name, vnfr_dict, vnfr_msg, vnfd): self._vnfr = vnfr_dict self._vnfr_msg = vnfr_msg + self._vnfd_msg = vnfd self._nsr_name = nsr_name self._configurable = False @@ -107,6 +149,10 @@ class RiftCMvnfr(object): def vnfr_msg(self): return self._vnfr_msg + @property + def vnfd(self): + return self._vnfd_msg + @property def name(self): return self._vnfr['short_name'] @@ -275,6 +321,119 @@ class RiftCMConfigPluginBase(object): """Get the status of the service""" return None + # Helper functions + + def convert_value(self, value, type_='STRING'): + if type_ == 'STRING': + return str(value) + + if type_ == 'INTEGER': + return int(value) + + if type_ == 'BOOLEAN': + return (value == 1) or (value.lower() == 'true') + + return value + + @asyncio.coroutine + def _read_dts(self, xpath, do_trace=False): + self._log.debug("_read_dts path = %s", xpath) + flags = rwdts.XactFlag.MERGE + res_iter = yield from self._dts.query_read( + xpath, flags=flags + ) + + results = [] + try: + for i in res_iter: + result = yield from i + if result is not None: + results.append(result.result) + except: + pass + + return results + + + @asyncio.coroutine + def get_xpath(self, xpath): + self._log.debug("Attempting to get xpath: {}".format(xpath)) + resp = yield from self._read_dts(xpath, False) + if len(resp) > 0: + self._log.debug("Got DTS resp: {}".format(resp[0])) + return resp[0] + return None + + @asyncio.coroutine + def get_nsr(self, id): + self._log.debug("Attempting to get NSR: %s", id) + nsrl = yield from self._read_dts(XPaths.nsr_opdata(id), False) + nsr = None + if len(nsrl) > 0: + nsr = nsrl[0].as_dict() + return nsr + + @asyncio.coroutine + def get_nsr_config(self, id): + self._log.debug("Attempting to get config NSR: %s", id) + nsrl = yield from self._read_dts(XPaths.nsr_config(id), False) + nsr = None + if len(nsrl) > 0: + nsr = nsrl[0] + return nsr + + @asyncio.coroutine + def get_vnfr(self, id): + self._log.debug("Attempting to get VNFR: %s", id) + vnfrl = yield from self._read_dts(XPaths.vnfr_opdata(id), do_trace=False) + vnfr_msg = None + if len(vnfrl) > 0: + vnfr_msg = vnfrl[0] + return vnfr_msg + + @asyncio.coroutine + def exec_script(self, script, data): + """Execute a shell script with the data as yaml input file""" + self._log.debug("Execute script {} with data {}". + format(script, data)) + tmp_file = None + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_file.write(yaml.dump(data, default_flow_style=True) + .encode("UTF-8")) + + cmd = "{} {}".format(script, tmp_file.name) + self._log.debug("Running the CMD: {}".format(cmd)) + + try: + proc = yield from asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + rc = yield from proc.wait() + script_out, script_err = yield from proc.communicate() + + except Exception as e: + msg = "Script {} caused exception: {}". \ + format(script, e) + self._log.exception(msg) + rc = 1 + script_err = msg + script_out = '' + + finally: + # Remove the tempfile created + try: + if rc == 0: + os.remove(tmp_file.name) + except OSError as e: + self._log.info("Error removing tempfile {}: {}". + format(tmp_file.name, e)) + + self._log.debug("Script {}: rc={}\nStdOut:{}\nStdErr:{}". + format(script, rc, script_out, script_err)) + + return rc, script_err + @asyncio.coroutine def invoke(self, method, *args): try: @@ -313,6 +472,8 @@ class RiftCMConfigPluginBase(object): self._log.error("Unknown method %s invoked on config agent plugin", method) except Exception as e: - self._log.error("Caught exception while invoking method: %s, Exception: %s", method, str(e)) - raise + self._log.exception("Caught exception while invoking method: %s, " + "Exception: %s", method, str(e)) + raise e + return rc diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py index 543e51b0..7fa50421 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py @@ -133,9 +133,9 @@ class RiftCMConfigAgent(object): rc = yield from agent.invoke(method, nsr, vnfr, *args) break except Exception as e: - self._log.error("Error invoking {} on {} : {}". - format(method, agent.name, e)) - raise + self._log.exception("Error invoking {} on {} : {}". + format(method, agent.name, e)) + raise e self._log.info("vnfr({}), method={}, return rc={}" .format(vnfr.name, method, rc)) diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py index 4848e9ee..bc13b178 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py @@ -205,7 +205,7 @@ class ConfigManagerConfig(object): done = False if 'nsrid' in task: nsrid = task['nsrid'] - self._log.debug("Will execute pending task for NSR id(%s)", nsrid) + self._log.debug("Will execute pending task for NSR id: %s", nsrid) try: # Try to configure this NSR task['retries'] -= 1 @@ -220,7 +220,7 @@ class ConfigManagerConfig(object): self.pending_tasks.remove(task) if done: - self._log.debug("Finished pending task NSR id(%s):", nsrid) + self._log.debug("Finished pending task NSR id: %s", nsrid) else: self._log.error("Failed configuring NSR(%s), retries remained:%d!", nsrid, task['retries']) @@ -241,7 +241,7 @@ class ConfigManagerConfig(object): # Initialize all handles that needs to be registered for reg in self.reg_handles: yield from reg.register() - + @asyncio.coroutine def register_cm_state_opdata(self): @@ -292,16 +292,16 @@ class ConfigManagerConfig(object): except Exception as e: self._log.error("Failed to register for opdata as (%s)", e) + def get_config_method(self, vnf_config): + cfg_types = ['netconf', 'juju', 'script'] + for method in cfg_types: + if method in vnf_config: + return method + return None + @asyncio.coroutine def process_nsd_vnf_configuration(self, nsr_obj, vnfr): - def get_config_method(vnf_config): - cfg_types = ['netconf', 'juju', 'script'] - for method in cfg_types: - if method in vnf_config: - return method - return None - def get_cfg_file_extension(method, configuration_options): ext_dict = { "netconf" : "xml", @@ -349,6 +349,9 @@ class ConfigManagerConfig(object): target : running ''' + # Get vnf_configuration from vnfr + vnf_config = vnfr['vnf_configuration'] + # Save some parameters needed as short cuts in flat structure (Also generated) vnf_cfg = vnfr['vnf_cfg'] # Prepare unique name for this VNF @@ -360,9 +363,6 @@ class ConfigManagerConfig(object): nsr_vnfr = '{}/{}_{}'.format( vnf_cfg['nsr_name'], vnfr['short_name'], vnfr['member_vnf_index_ref']) - # Get vnf_configuration from vnfr - vnf_config = vnfr['vnf_configuration'] - self._log.debug("vnf_configuration = %s", vnf_config) # Create priority dictionary @@ -375,7 +375,7 @@ class ConfigManagerConfig(object): # No VNFR with this priority yet, initialize the list nsr_obj.nsr_cfg_config_attributes_dict[cfg_priority_order] = [] - method = get_config_method(vnf_config) + method = self.get_config_method(vnf_config) if method is not None: # Create all sub dictionaries first config_priority = { @@ -384,9 +384,12 @@ class ConfigManagerConfig(object): 'member_vnf_index' : vnfr['member_vnf_index_ref'], } - if 'config_delay' in vnf_config['config_attributes']: - config_priority['configuration_delay'] = vnf_config['config_attributes']['config_delay'] - vnf_cfg['config_delay'] = config_priority['configuration_delay'] + if ('config_attributes' in vnf_config) and \ + ('config_delay' in vnf_config['config_attributes']): + config_priority['configuration_delay'] = vnf_config['config_attributes']['config_delay'] + vnf_cfg['config_delay'] = config_priority['configuration_delay'] + else: + vnf_cfg['config_delay'] = 0 configuration_options = {} self._log.debug("config method=%s", method) @@ -416,16 +419,14 @@ class ConfigManagerConfig(object): vnf_cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address'] vnf_cp_dict['rw_username'] = vnf_cfg['username'] vnf_cp_dict['rw_password'] = vnf_cfg['password'] - - # TBD - see if we can neatly include the config in "config_attributes" file, no need though - #config_priority['config_template'] = vnf_config['config_template'] - # Create config file - vnf_cfg['juju_script'] = os.path.join(self._parent.cfg_dir, 'juju_if.py') if 'config_template' in vnf_config: - vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(nsr_obj.cfg_path_prefix, config_priority['configuration_type']) - vnf_cfg['cfg_file'] = '{}.{}'.format(nsr_obj.cfg_path_prefix, get_cfg_file_extension(method, configuration_options)) + vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(nsr_obj.cfg_path_prefix, + config_priority['configuration_type']) + vnf_cfg['cfg_file'] = '{}.{}'.format(nsr_obj.cfg_path_prefix, + get_cfg_file_extension(method, + configuration_options)) vnf_cfg['xlate_script'] = os.path.join(self._parent.cfg_dir, 'xlate_cfg.py') try: # Now write this template into file @@ -456,6 +457,338 @@ class ConfigManagerConfig(object): # Update the cm-state nsr_obj.populate_vm_state_from_vnf_cfg() + @asyncio.coroutine + def update_config_primitives(self, nsr_obj): + + # Process all config-primitives in the member VNFs + for vnfr in nsr_obj.vnfrs: + vnfd = vnfr['vnf_cfg']['agent_vnfr'].vnfd + + try: + prims = vnfd.vnf_configuration.config_primitive + if not prims: + self._log.debug("VNFR {} with VNFD {} has no config primitives defined". + format(vnfr.name, vnfd.name)) + return + except AttributeError as e: + self._log.error("No config primitives found on VNFR {} ({})". + format(vnfr.name, vnfd.name)) + + cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id']) + srcs = cm_state['config_parameter']['config_parameter_source'] + reqs = cm_state['config_parameter']['config_parameter_request'] + + vnf_configuration = vnfd.vnf_configuration.as_dict() + vnf_configuration['config_primitive'] = [] + for prim in prims: + confp = prim.as_dict() + for param in confp['parameter']: + # First check the param in capabilities + found = False + for src in srcs: + for p in src['parameter']: + if (p['config_primitive_ref'] == confp['name']) \ + and (p['parameter_ref'] == param['name']): + param['default_value'] = src['value'] + param['read_only'] = True + found = True + break + if found: + break + + if not found: + for req in reqs: + for p in req['parameter']: + if (p['config_primitive_ref'] == confp['name']) \ + and (p['parameter_ref'] == param['name']): + param['default_value'] = req['value'] + param['read_only'] = True + found = True + break + if found: + break + + self._log.debug("Config primitive: {}".format(confp)) + vnf_configuration['config_primitive'].append(confp) + + cm_state['vnf_configuration'] = vnf_configuration + + @asyncio.coroutine + def get_resolved_xpath(self, xpath, name, vnf_name, xpath_prefix): + # For now, use DTS to resolve the path + # TODO (pjoseph): Add better xpath support + + dts_path = xpath + if xpath.startswith('../'): + prefix = xpath_prefix + xp = xpath + while xp.startswith('../'): + idx = prefix.rfind('/') + if idx == -1: + raise ValueError("VNF {}, Did not find the xpath specified: {}". + format(vnf_name, xpath)) + prefix = prefix[:idx] + xp = xp[3:] + + dts_path = prefix + '/' + xp + + elif xpath.startswith('/'): + dts_path = 'C,' + xpath + elif xpath.startswith('C,/') or xpath.startswith('D,/'): + dts_path = xpath + else: + self._log.error("Invalid xpath {} for source {} in VNF {}". + format(xpath, name, vnf_name)) + raise ValueError("Descriptor xpath {} in source {} for VNF {} " + "is invalid". + format(xpath, name, vnf_name)) + return dts_path + + @asyncio.coroutine + def resolve_xpath(self, xpath, name, vnfd): + xpath_prefix = "C,/vnfd:vnfd-catalog/vnfd[id='{}']/config-parameter" \ + "/config-parameter-source/config-parameter-source[name='{}']" \ + "/descriptor".format(vnfd.id, name) + + dts_path = yield from self.get_resolved_xpath(xpath, name, + vnfd.name, xpath_prefix) + idx = dts_path.rfind('/') + if idx == -1: + raise ValueError("VNFD {}, descriptor xpath {} should point to " \ + "an attribute".format(vnfd.name, xpath)) + + attr = dts_path[idx+1:] + dts_path = dts_path[:idx] + self._log.debug("DTS path: {}, attribute: {}".format(dts_path, attr)) + + resp = yield from self.cmdts_obj.get_xpath(dts_path) + if resp is None: + raise ValueError("Xpath {} in capability {} for VNFD {} is not found". + format(xpath, name, vnfd.name)) + self._log.debug("DTS response: {}".format(resp.as_dict())) + + try: + val = getattr(resp, attr) + except AttributeError as e: + self._log.error("Did not find attribute : {}".format(attr)) + try: + val = getattr(resp, attr.replace('-', '_')) + except AttributeError as e: + raise ValueError("Did not find attribute {} in XPath {} " + "for capability {} in VNF {}". + format(attr, dts_path, vnfd.name)) + + self._log.debug("XPath {}: {}".format(xpath, val)) + return val + + @asyncio.coroutine + def resolve_attribute(self, attribute, name, vnfd, vnfr): + idx = attribute.rfind(',') + if idx == -1: + raise ValueError ("Invalid attribute {} for capability {} in " + "VNFD specified". + format(attribute, name, vnfd.name)) + xpath = attribute[:idx].strip() + attr = attribute[idx+1:].strip() + self._log.debug("Attribute {}, {}".format(xpath, attr)) + if xpath.startswith('C,/'): + raise ValueError("Attribute {} for capability {} in VNFD cannot " + "be a config". + format(attribute, name, vnfd.name)) + + xpath_prefix = "D,/vnfr:vnfr-catalog/vnfr[id='{}']/config_parameter" \ + "/config-parameter-source/config-parameter-source[name='{}']" \ + "/attribute".format(vnfr['id'], name) + dts_path = yield from self.get_resolved_xpath(xpath, name, + vnfr['name'], + xpath_prefix) + self._log.debug("DTS query: {}".format(dts_path)) + + resp = yield from self.cmdts_obj.get_xpath(dts_path) + if resp is None: + raise ValueError("Attribute {} in request {} for VNFD {} is " \ + "not found". + format(xpath, name, vnfd.name)) + self._log.debug("DTS response: {}".format(resp.as_dict())) + + try: + val = getattr(resp, attr) + except AttributeError as e: + self._log.debug("Did not find attribute {}".format(attr)) + try: + val = getattr(resp, attr.replace('-', '_')) + except AttributeError as e: + raise ValueError("Did not find attribute {} in XPath {} " + "for source {} in VNF {}". + format(attr, dts_path, vnfd.name)) + + self._log.debug("Attribute {}: {}".format(attribute, val)) + return val + + @asyncio.coroutine + def process_vnf_config_parameter(self, nsr_obj): + nsd = nsr_obj.agent_nsr.nsd + + # Process all capabilities in all the member VNFs + for vnfr in nsr_obj.vnfrs: + vnfd = vnfr['vnf_cfg']['agent_vnfr'].vnfd + + try: + cparam = vnfd.config_parameter + except AttributeError as e: + self._log.debug("VNFR {} does not have VNF config parameter". + format(vnfr.name)) + continue + + srcs = [] + try: + srcs = cparam.config_parameter_source + except AttributeError as e: + self._log.debug("VNFR {} has no source defined". + format(vnfr.name)) + + # Get the cm state dict for this vnfr + cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id']) + + cm_srcs = [] + for src in srcs: + self._log.debug("VNFR {}: source {}". + format(vnfr['name'], src.as_dict())) + + param_refs = [] + for p in src.parameter: + param_refs.append({ + 'config_primitive_ref': p.config_primitive_name_ref, + 'parameter_ref': p.config_primitive_parameter_ref + }) + + try: + val = src.value + self._log.debug("Got value {}".format(val)) + if val: + cm_srcs.append({'name': src.name, + 'value': str(val), + 'parameter': param_refs}) + continue + except AttributeError as e: + pass + + try: + xpath = src.descriptor + # resolve xpath + if xpath: + val = yield from self.resolve_xpath(xpath, src.name, vnfd) + self._log.debug("Got xpath value: {}".format(val)) + cm_srcs.append({'name': src.name, + 'value': str(val), + 'parameter': param_refs}) + continue + except AttributeError as e: + pass + + try: + attribute = src.attribute + # resolve attribute + if attribute: + val = yield from self.resolve_attribute(attribute, + src.name, + vnfd, vnfr) + self._log.debug("Got attribute value: {}".format(val)) + cm_srcs.append({'name': src.name, + 'value': str(val), + 'parameter': param_refs}) + continue + except AttributeError as e: + pass + + try: + prim = src.primitive_ref + if prim: + raise NotImplementedError("{}: VNF config parameter {}" + "source support for config" + "primitive not yet supported". + format(vnfr.name, prim)) + except AttributeError as e: + pass + + self._log.debug("VNF config parameter sources: {}".format(cm_srcs)) + cm_state['config_parameter']['config_parameter_source'] = cm_srcs + + try: + reqs = cparam.config_parameter_request + except AttributeError as e: + self._log.debug("VNFR {} has no requests defined". + format(vnfr.name)) + continue + + cm_reqs = [] + for req in reqs: + self._log.debug("VNFR{}: request {}". + format(vnfr['name'], req.as_dict())) + param_refs = [] + for p in req.parameter: + param_refs.append({ + 'config_primitive_ref': p.config_primitive_name_ref, + 'parameter_ref': p.config_primitive_parameter_ref + }) + cm_reqs.append({'name': req.name, + 'parameter': param_refs}) + + self._log.debug("VNF requests: {}".format(cm_reqs)) + cm_state['config_parameter']['config_parameter_request'] = cm_reqs + + # Publish all config parameter for the VNFRs + # yield from nsr_obj.publish_cm_state() + + cparam_map = [] + try: + cparam_map = nsd.config_parameter_map + except AttributeError as e: + self._log.debug("No config parameter map specified for nsr: {}". + format(nsr_obj.nsr_name)) + + for cp in cparam_map: + src_vnfr = nsr_obj.agent_nsr.get_member_vnfr( + cp.config_parameter_source.member_vnf_index_ref) + cm_state = nsr_obj.find_vnfr_cm_state(src_vnfr.id) + if cm_state is None: + raise ValueError("Config parameter sources are not defined " + "for VNF member {} ({})". + format(cp.config_parameter_source.member_vnf_index_ref, + src_vnfr.name)) + srcs = cm_state['config_parameter']['config_parameter_source'] + + src_attr = cp.config_parameter_source.config_parameter_source_ref + val = None + for src in srcs: + if src['name'] == src_attr: + val = src['value'] + break + + req_vnfr = nsr_obj.agent_nsr.get_member_vnfr( + cp.config_parameter_request.member_vnf_index_ref) + req_attr = cp.config_parameter_request.config_parameter_request_ref + cm_state = nsr_obj.find_vnfr_cm_state(req_vnfr.id) + try: + cm_reqs = cm_state['config_parameter']['config_parameter_request'] + except KeyError as e: + raise ValueError("VNFR index {} ({}) has no requests defined". + format(cp.config_parameter_reequest.member_vnf_index_ref, + req_vnfr['name'])) + + for i, item in enumerate(cm_reqs): + if item['name'] == req_attr: + item['value'] = str(val) + cm_reqs[i] = item + self._log.debug("Request in VNFR {}: {}". + format(req_vnfr.name, item)) + break + + yield from self.update_config_primitives(nsr_obj) + + # Publish resolved dependencies for the VNFRs + yield from nsr_obj.publish_cm_state() + @asyncio.coroutine def config_NSR(self, id): @@ -463,14 +796,14 @@ class ConfigManagerConfig(object): yaml_dict = dict(sorted(config_attributes_dict.items())) yf.write(yaml.dump(yaml_dict, default_flow_style=False)) - + nsr_dict = self._nsr_dict self._log.info("Configure NSR, id = %s", id) #####################TBD########################### # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_create_nsr', self.id, self._nsd) # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_nsr_active', self.id, self._vnfrs) - + try: if id not in nsr_dict: nsr_obj = ConfigManagerNSR(self._log, self._loop, self, id) @@ -486,7 +819,7 @@ class ConfigManagerConfig(object): if nsr_obj.cm_nsr['state'] != nsr_obj.state_to_string(conmanY.RecordState.INIT): self._log.debug("NSR(%s) is already processed, state=%s", nsr_obj.nsr_name, nsr_obj.cm_nsr['state']) - yield from nsr_obj.publish_cm_state() + #yield from nsr_obj.publish_cm_state() return True cmdts_obj = self.cmdts_obj @@ -519,22 +852,31 @@ class ConfigManagerConfig(object): # return nsr_obj.set_config_dir(self) - + for const_vnfr in nsr['constituent_vnfr_ref']: self._log.debug("Fetching VNFR (%s)", const_vnfr['vnfr_id']) vnfr_msg = yield from cmdts_obj.get_vnfr(const_vnfr['vnfr_id']) if vnfr_msg: vnfr = vnfr_msg.as_dict() + # Get the VNFD also + vnfd = vnfr_msg.vnfd + self._log.info("create VNF:{}/{}".format(nsr_obj.nsr_name, vnfr['short_name'])) - agent_vnfr = yield from nsr_obj.add_vnfr(vnfr, vnfr_msg) + agent_vnfr = yield from nsr_obj.add_vnfr(vnfr, vnfr_msg, vnfd) # Preserve order, self.process_nsd_vnf_configuration() # sets up the config agent based on the method yield from self.process_nsd_vnf_configuration(nsr_obj, vnfr) - yield from self._config_agent_mgr.invoke_config_agent_plugins( - 'notify_create_vnfr', - nsr_obj.agent_nsr, - agent_vnfr) + + # Process VNF config parameter + yield from self.process_vnf_config_parameter(nsr_obj) + + # Invoke the config agent plugin + for vnfr in nsr_obj.vnfrs: + yield from self._config_agent_mgr.invoke_config_agent_plugins( + 'notify_create_vnfr', + nsr_obj.agent_nsr, + vnfr['vnf_cfg']['agent_vnfr']) #####################TBD########################### # self._log.debug("VNF active. Apply initial config for vnfr {}".format(vnfr.name)) @@ -583,7 +925,7 @@ class ConfigManagerConfig(object): # Iterate through each priority level for vnf_config_attributes_dict in config_attributes_dict: # Iterate through each vnfr at this priority level - + # Make up vnf_unique_name with vnfd name and member index #vnfr_name = "{}.{}".format(nsr_obj.nsr_name, vnf_config_attributes_dict['name']) vnf_unique_name = get_vnf_unique_name( @@ -596,7 +938,8 @@ class ConfigManagerConfig(object): # Find vnfr for this vnf_unique_name if vnf_unique_name not in nsr_obj._vnfr_dict: - self._log.error("NS (%s) - Can not find VNF to be configured: %s", nsr_obj.nsr_name, vnf_unique_name) + self._log.error("NS (%s) - Can not find VNF to be configured: %s", + nsr_obj.nsr_name, vnf_unique_name) else: # Save this unique VNF's config input parameters nsr_obj.vnf_config_attributes_dict[vnf_unique_name] = vnf_config_attributes_dict @@ -643,7 +986,8 @@ class ConfigManagerConfig(object): if nsr_obj_p == nsr_obj: assert id == nsr_obj_p._nsr_id #self._parent.pending_cfg.remove(nsr_obj_p) - # Mark this as being deleted so we do not try to configure it if we are in cfg_delay (will wake up and continue to process otherwise) + # Mark this as being deleted so we do not try to configure it + # if we are in cfg_delay (will wake up and continue to process otherwise) nsr_obj_p.being_deleted = True self._log.info("Removed scheduled configuration for NSR(%s)", nsr_obj.nsr_name) @@ -802,12 +1146,18 @@ class ConfigManagerConfig(object): self._log.debug("Running the CMD: {}".format(cmd)) process = yield from asyncio. \ - create_subprocess_shell(cmd, loop=self._loop) + create_subprocess_shell( + cmd, loop=self._loop, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) yield from process.wait() if process.returncode: + script_out, script_err = yield from proc.communicate() msg = "NSR {} initial config using {} failed with {}". \ format(nsr_name, script, process.returncode) self._log.error(msg) + self._log.error("Script {} stderr: {}". + format(script, script_err)) raise InitialConfigError(msg) else: os.remove(inp_file) @@ -905,7 +1255,7 @@ class ConfigManagerNSR(object): caller._nsr['name_ref'], self.this_nsr_dir) self.config_attributes_file = os.path.join(self.this_nsr_dir, "configuration_config_attributes.yml") self.xlate_dict_file = os.path.join(self.this_nsr_dir, "nsr_xlate_dict.yml") - + def xlate_conf(self, vnfr, vnf_cfg): # If configuration type is not already set, try to read from attributes @@ -964,30 +1314,31 @@ class ConfigManagerNSR(object): self._cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address'] self._cp_dict['rw_username'] = vnf_cfg['username'] self._cp_dict['rw_password'] = vnf_cfg['password'] - ############################################################ - # TBD - Need to lookup above 3 for a given VNF, not global # - # Once we do that no need to dump below file again before # - # each VNF configuration translation. # - # This will require all existing config templates to be # - # changed for above three tags to include member index # - ############################################################ - try: - nsr_obj = vnf_cfg['nsr_obj'] - # Generate config_config_attributes.yaml (For debug reference) - with open(nsr_obj.xlate_dict_file, "w") as yf: - yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False)) - except Exception as e: - self._log.error("NS:(%s) failed to write nsr xlate tags file as (%s)", nsr_obj.nsr_name, str(e)) - + if 'cfg_template' in vnf_cfg: - script_cmd = 'python3 {} -i {} -o {} -x "{}"'.format(vnf_cfg['xlate_script'], vnf_cfg['cfg_template'], vnf_cfg['cfg_file'], self.xlate_dict_file) - self._log.debug("xlate script command (%s)", script_cmd) - #xlate_msg = subprocess.check_output(script_cmd).decode('utf-8') - xlate_msg = subprocess.check_output(script_cmd, shell=True).decode('utf-8') - self._log.info("xlate script output (%s)", xlate_msg) + try: + nsr_obj = vnf_cfg['nsr_obj'] + # Generate config_config_attributes.yaml (For debug reference) + with open(nsr_obj.xlate_dict_file, "w") as yf: + yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False)) + except Exception as e: + self._log.error("NS:(%s) failed to write nsr xlate tags " + "file as (%s)", nsr_obj.nsr_name, str(e)) + + if 'cfg_template' in vnf_cfg: + script_cmd = 'python3 {} -i {} -o {} -x "{}"'. \ + format(vnf_cfg['xlate_script'], + vnf_cfg['cfg_template'], + vnf_cfg['cfg_file'], + self.xlate_dict_file) + self._log.debug("xlate script command (%s)", script_cmd) + xlate_msg = subprocess.check_output(script_cmd, shell=True).decode('utf-8') + self._log.info("xlate script output (%s)", xlate_msg) + except Exception as e: vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_PROCESS_FAILED) - self._log.error("Failed to execute translation script for VNF: %s with (%s)", log_this_vnf(vnf_cfg), str(e)) + self._log.error("Failed to execute translation script for VNF: %s with (%s)", + log_this_vnf(vnf_cfg), str(e)) return self._log.info("Applying config to VNF: %s = %s!", log_this_vnf(vnf_cfg), vnf_cfg) @@ -1113,6 +1464,11 @@ class ConfigManagerNSR(object): 'cfg_type' : vnf_cfg['config_method'], 'cfg_location' : vnf_cfg['cfg_file'], 'connection_point' : [], + 'config_parameter' : + { + 'config_parameter_source' : [], + 'config_parameter_request' : [], + }, } self.cm_nsr['cm_vnfr'].append(vnf_cm_state) @@ -1172,7 +1528,7 @@ class ConfigManagerNSR(object): yield from self.publish_cm_state() @asyncio.coroutine - def add_vnfr(self, vnfr, vnfr_msg): + def add_vnfr(self, vnfr, vnfr_msg, vnfd): @asyncio.coroutine def populate_subnets_from_vlr(id): @@ -1188,13 +1544,14 @@ class ConfigManagerNSR(object): except Exception as e: self._log.error("VNF:(%s) VLR Error = %s", vnfr['short_name'], e) - + if vnfr['id'] not in self._vnfr_dict: self._log.info("NSR(%s) : Adding VNF Record for name=%s, id=%s", self._nsr_id, vnfr['short_name'], vnfr['id']) # Add this vnfr to the list for show, or single traversal self._vnfr_list.append(vnfr) else: - self._log.warning("NSR(%s) : VNF Record for name=%s, id=%s already exists, overwriting", self._nsr_id, vnfr['short_name'], vnfr['id']) + self._log.warning("NSR(%s) : VNF Record for name=%s, id=%s already exists, overwriting", + self._nsr_id, vnfr['short_name'], vnfr['id']) # Make vnfr available by id as well as by name unique_name = get_vnf_unique_name(self.nsr_name, vnfr['short_name'], vnfr['member_vnf_index_ref']) @@ -1205,7 +1562,7 @@ class ConfigManagerNSR(object): vnf_cfg = { 'nsr_obj' : self, 'vnfr' : vnfr, - 'agent_vnfr' : self.agent_nsr.add_vnfr(vnfr, vnfr_msg), + 'agent_vnfr' : self.agent_nsr.add_vnfr(vnfr, vnfr_msg, vnfd), 'nsr_name' : self.nsr_name, 'nsr_id' : self._nsr_id, 'vnfr_name' : vnfr['short_name'], @@ -1270,7 +1627,7 @@ class ConfigManagerNSR(object): if 'internal_vlr' in vnfr: for ivlr in vnfr['internal_vlr']: yield from populate_subnets_from_vlr(ivlr['vlr_ref']) - + # Update vnfr vnf_cfg['agent_vnfr']._vnfr = vnfr return vnf_cfg['agent_vnfr'] @@ -1292,6 +1649,11 @@ class XPaths(object): return ("D,/vnfr:vnfr-catalog/vnfr:vnfr" + ("[vnfr:id='{}']".format(k) if k is not None else "")) + @staticmethod + def vnfd_path(k=None): + return ("C,/vnfd:vnfd-catalog/vnfd:vnfd" + + ("[vnfd:id='{}']".format(k) if k is not None else "")) + @staticmethod def config_agent(k=None): return ("D,/rw-config-agent:config-agent/rw-config-agent:account" + @@ -1334,6 +1696,15 @@ class ConfigManagerDTS(object): return results + @asyncio.coroutine + def get_xpath(self, xpath): + self._log.debug("Attempting to get xpath: {}".format(xpath)) + resp = yield from self._read_dts(xpath, False) + if len(resp) > 0: + self._log.debug("Got DTS resp: {}".format(resp[0])) + return resp[0] + return None + @asyncio.coroutine def get_nsr(self, id): self._log.debug("Attempting to get NSR: %s", id) @@ -1377,6 +1748,15 @@ class ConfigManagerDTS(object): vnfr_msg = vnfrl[0] return vnfr_msg + @asyncio.coroutine + def get_vnfd(self, id): + self._log.debug("Attempting to get VNFD: %s", XPaths.vnfd_path(id)) + vnfdl = yield from self._read_dts(XPaths.vnfd_path(id), do_trace=False) + vnfd_msg = None + if len(vnfdl) > 0: + vnfd_msg = vnfdl[0] + return vnfd_msg + @asyncio.coroutine def get_vlr(self, id): self._log.debug("Attempting to get VLR subnet: %s", id) @@ -1414,7 +1794,7 @@ class ConfigManagerDTS(object): def register(self): yield from self.register_to_publish() yield from self.register_for_nsr() - + @asyncio.coroutine def register_to_publish(self): ''' Register to DTS for publishing cm-state opdata ''' diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py index f292a689..0b2e5763 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py @@ -55,9 +55,12 @@ def log_this_vnf(vnf_cfg): else: log_vnf += "{}/".format(vnf_cfg[item]) return log_vnf - + + class ConfigManagerROifConnectionError(Exception): pass + + class ScriptError(Exception): pass @@ -78,23 +81,23 @@ class ConfigManagerEvents(object): def update_vnf_state(self, vnf_cfg, state): nsr_obj = vnf_cfg['nsr_obj'] yield from nsr_obj.update_vnf_cm_state(vnf_cfg['vnfr'], state) - + @asyncio.coroutine def apply_vnf_config(self, vnf_cfg): self._log.debug("apply_vnf_config VNF:{}" .format(log_this_vnf(vnf_cfg))) - + if vnf_cfg['config_delay']: yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_DELAY) yield from asyncio.sleep(vnf_cfg['config_delay'], loop=self._loop) - + # See if we are still alive! if vnf_cfg['nsr_obj'].being_deleted: # Don't do anything, just return self._log.info("VNF : %s is being deleted, skipping configuration!", log_this_vnf(vnf_cfg)) return True - + yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_SEND) try: if vnf_cfg['config_method'] == 'netconf': @@ -124,21 +127,20 @@ class ConfigManagerEvents(object): yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_FAILED) return True - #Update VNF state - yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.READY) - self._log.info("Successfully applied configuration to VNF: %s", + self._log.info("Successfully applied config template to VNF: %s", log_this_vnf(vnf_cfg)) + except Exception as e: self._log.error("Applying configuration(%s) file(%s) to VNF: %s failed as: %s", vnf_cfg['config_method'], vnf_cfg['cfg_file'], log_this_vnf(vnf_cfg), str(e)) - #raise + self._log.exception(e) return False return True - + class ConfigManagerVNFscriptconf(object): def __init__(self, log, loop, parent, vnf_cfg): @@ -151,7 +153,12 @@ class ConfigManagerVNFscriptconf(object): #@asyncio.coroutine def apply_edit_cfg(self): vnf_cfg = self._vnf_cfg - self._log.debug("Attempting to apply scriptconf to VNF: %s", log_this_vnf(vnf_cfg)) + self._log.debug("Attempting to apply scriptconf to VNF %s: %s", log_this_vnf(vnf_cfg), vnf_cfg) + + if vnf_cfg['cfg_file'] is None or (vnf_cfg['cfg_file'] == 'None'): + self._log.debug("Config file for script not provided") + return + try: st = os.stat(vnf_cfg['cfg_file']) os.chmod(vnf_cfg['cfg_file'], st.st_mode | stat.S_IEXEC) @@ -297,6 +304,10 @@ class ConfigManagerVNFnetconf(object): vnf_cfg = self._vnf_cfg self._log.debug("Attempting to apply netconf to VNF: %s", log_this_vnf(vnf_cfg)) + if vnf_cfg['cfg_file'] is None or (vnf_cfg['cfg_file'] == 'None'): + self._log.debug("Config file for Netconf not provided") + return + if self._manager is None: self._log.error("Netconf is not connected to VNF: %s, aborting!", log_this_vnf(vnf_cfg)) return diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py index 7ea73c49..376814b2 100755 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py @@ -225,8 +225,8 @@ class ConfigurationManager(object): ids.append(nsr_id) e = task.exception() if e: - self._log.error("Exception in configuring nsr {}: {}". - format(nsr_id, e)) + self._log.exception("Exception in configuring nsr {}: {}". + format(nsr_id, e)) nsr_obj = self.get_nsr_obj(nsr_id) if nsr_obj: yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_FAILED, str(e)) diff --git a/rwcm/plugins/yang/rw-conman.yang b/rwcm/plugins/yang/rw-conman.yang index bb1555d0..daaa8d06 100644 --- a/rwcm/plugins/yang/rw-conman.yang +++ b/rwcm/plugins/yang/rw-conman.yang @@ -253,6 +253,70 @@ module rw-conman type inet:ip-address; } } + // Publish the resolved capabilites and dependecies here + container config-parameter { + description + "The resolved config parameter for a VNF"; + list config-parameter-source { + description "List of resolved sources"; + key "name"; + + leaf name { + type string { + length 128; + } + description + "Name of the source as defined in VNFD"; + } + leaf value { + type string; + description + "Resovled value for the source"; + } + list parameter { + key "config-primitive-ref"; + leaf config-primitive-ref { + type string; + } + leaf parameter-ref { + type string; + } + } + } + + list config-parameter-request { + description + "List of resolved requests for the the VNF"; + key "name"; + + leaf name { + type string { + length 128; + } + description + "Name of the request as defined in VNFD"; + } + leaf value { + type string; + description + "Resovled value for the request"; + } + list parameter { + key "config-primitive-ref"; + leaf config-primitive-ref { + type string; + } + leaf parameter-ref { + type string; + } + } + } + } + + // Resolve the VNF config-primitives and publish it + // here. VNFR will use this and update config-primitives + // from here. + uses manotypes:vnf-configuration; } // list VNFR } // list NSR } // cm-state diff --git a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py index 4ab351e7..5beafe60 100644 --- a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py +++ b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py @@ -628,9 +628,15 @@ class MonitorTasklet(rift.tasklets.Tasklet): self.dts.handle.set_state(next_state) def on_vnfr_create(self, vnfr): - if not self.monitor.nfvi_metrics_available(vnfr.cloud_account): + try: + acc = vnfr.cloud_account + except AttributeError as e: + self.log.warning("NFVI metrics not supported") + return + + if not self.monitor.nfvi_metrics_available(acc): msg = "NFVI metrics unavailable for {}" - self.log.warning(msg.format(vnfr.cloud_account)) + self.log.warning(msg.format(acc)) return self.monitor.add_vnfr(vnfr) @@ -642,6 +648,12 @@ class MonitorTasklet(rift.tasklets.Tasklet): self.loop.create_task(coro) def on_vnfr_update(self, vnfr): + try: + acc = vnfr.cloud_account + except AttributeError as e: + self.log.warning("NFVI metrics not supported") + return + if not self.monitor.nfvi_metrics_available(vnfr.cloud_account): msg = "NFVI metrics unavailable for {}" self.log.warning(msg.format(vnfr.cloud_account)) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py index 23ab7b6e..8073c4cf 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py @@ -83,33 +83,38 @@ class ROConfigManager(object): return try: - nsrid = cm_nsr['id'] + nsrid = cm_nsr.id # Update the VNFRs' config status - gen = [] - if 'cm_vnfr' in cm_nsr: - gen = (vnfr for vnfr in cm_nsr['cm_vnfr'] - if vnfr['id'] in self.nsm._vnfrs) + gen = (vnfr for vnfr in cm_nsr.cm_vnfr + if vnfr.id in self.nsm._vnfrs) for vnfr in gen: - vnfrid = vnfr['id'] - new_status = ROConfigManager.map_config_status(vnfr['state']) + vnfrid = vnfr.id + new_status = ROConfigManager.map_config_status(vnfr.state) self._log.debug("Updating config status of VNFR {} " \ "in NSR {} to {}({})". format(vnfrid, nsrid, new_status, - vnfr['state'])) + vnfr.state)) yield from \ self.nsm.vnfrs[vnfrid].set_config_status(new_status) + yield from \ + self.nsm.vnfrs[vnfrid].update_config_primitives( + vnfr.vnf_configuration) + # Update the NSR's config status - new_status = ROConfigManager.map_config_status(cm_nsr['state']) - self._log.info("Updating config status of NSR {} to {}({})". - format(nsrid, new_status, cm_nsr['state'])) + new_status = ROConfigManager.map_config_status(cm_nsr.state) + self._log.debug("Updating config status of NSR {} to {}({})". + format(nsrid, new_status, cm_nsr.state)) - # If terminate nsr request comes when NS instantiation is in 'Configuring state'; self.nsm.nsrs dict - # is already empty when self.nsm.nsrs[nsrid].set_config_status gets executed. So adding a check here. + # If terminate nsr request comes when NS instantiation is in + # 'Configuring state'; self.nsm.nsrs dict is already empty when + # self.nsm.nsrs[nsrid].set_config_status gets executed. So adding a check here. if nsrid in self.nsm.nsrs: - yield from self.nsm.nsrs[nsrid].set_config_status(new_status, cm_nsr.get('state_details')) + yield from self.nsm.nsrs[nsrid].set_config_status( + new_status, + cm_nsr.state_details) except Exception as e: self._log.error("Failed to process cm-state for nsr {}: {}". @@ -119,12 +124,11 @@ class ROConfigManager(object): @asyncio.coroutine def register(self): """ Register for cm-state changes """ - + @asyncio.coroutine def on_prepare(xact_info, query_action, ks_path, msg): """ cm-state changed """ - #print("###>>> cm-state change ({}), msg_dict = {}".format(query_action, msg_dict)) self._log.debug("Received cm-state on_prepare (%s:%s:%s)", query_action, ks_path, @@ -133,10 +137,11 @@ class ROConfigManager(object): if (query_action == rwdts.QueryAction.UPDATE or query_action == rwdts.QueryAction.CREATE): # Update Each NSR/VNFR state - msg_dict = msg.as_dict() - yield from self.update_ns_cfg_state(msg_dict) + # msg_dict = msg.as_dict() + yield from self.update_ns_cfg_state(msg) elif query_action == rwdts.QueryAction.DELETE: - self._log.debug("DELETE action in on_prepare for cm-state, ignoring") + self._log.debug("DELETE action in on_prepare for cm-state, " + "ignoring") else: raise NotImplementedError( "%s on cm-state is not supported", @@ -145,10 +150,13 @@ class ROConfigManager(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) try: - handler = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare) - self.dts_reg_hdl = yield from self._dts.register(self.cm_state_xpath, - flags=rwdts.Flag.SUBSCRIBER, - handler=handler) + handler = rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare) + self.dts_reg_hdl = yield from self._dts.register( + self.cm_state_xpath, + flags=rwdts.Flag.SUBSCRIBER, + handler=handler) + except Exception as e: - self._log.error("Failed to register for cm-state changes as %s", str(e)) - + self._log.error("Failed to register for cm-state changes as %s", + str(e)) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py index 2388b91d..16431be4 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py @@ -42,6 +42,7 @@ gi.require_version('RwNsrYang', '1.0') gi.require_version('RwTypes', '1.0') gi.require_version('RwVlrYang', '1.0') gi.require_version('RwVnfrYang', '1.0') +gi.require_version('VnfdYang', '1.0') from gi.repository import ( RwYang, RwNsrYang, @@ -54,6 +55,7 @@ from gi.repository import ( RwsdnYang, RwDts as rwdts, RwTypes, + VnfdYang, ProtobufC, ) @@ -830,7 +832,10 @@ class VirtualNetworkFunctionRecord(object): @property def const_vnfr_msg(self): """ VNFR message """ - return RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConstituentVnfrRef(vnfr_id=self.id,cloud_account=self.cloud_account_name,om_datacenter=self._om_datacenter_name) + return RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConstituentVnfrRef( + vnfr_id=self.id, + cloud_account=self.cloud_account_name, + om_datacenter=self._om_datacenter_name) @property def vnfd(self): @@ -981,7 +986,7 @@ class VirtualNetworkFunctionRecord(object): format(self.name, self.vnfr_msg)) yield from self._dts.query_update( self.xpath, - rwdts.XactFlag.TRACE, + rwdts.XactFlag.REPLACE, self.vnfr_msg ) @@ -1042,6 +1047,49 @@ class VirtualNetworkFunctionRecord(object): return False + @asyncio.coroutine + def update_config_primitives(self, vnf_config): + # Update only after we are configured + if self._config_status == NsrYang.ConfigStates.INIT: + return + + if not vnf_config.as_dict(): + return + + self._log.debug("Update VNFR {} config: {}". + format(self.name, vnf_config.as_dict())) + + # Update config primitive + updated = False + for prim in self._vnfd.vnf_configuration.config_primitive: + for p in vnf_config.config_primitive: + if prim.name == p.name: + for param in prim.parameter: + for pa in p.parameter: + if pa.name == param.name: + if pa.default_value and \ + (pa.default_value != param.default_value): + param.default_value = pa.default_value + param.read_only = pa.read_only + updated = True + break + self._log.debug("Prim: {}".format(prim.as_dict())) + break + + if updated: + self._log.debug("Updated VNFD {} config: {}". + format(self._vnfd.name, + self._vnfd.vnf_configuration)) + self._vnfr_msg = self.create_vnfr_msg() + + try: + yield from self.update_vnfm() + except Exception as e: + self._log.error("Exception updating VNFM with new config " + "primitive for VNFR {}: {}". + format(self.name, e)) + self._log.exception(e) + @asyncio.coroutine def instantiate(self, nsr): """ Instantiate this VNFR""" diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index 70522994..87f497dd 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -1336,6 +1336,9 @@ class VirtualNetworkFunctionRecord(object): vnfr_dict.update(vnfd_copy_dict) vnfr_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict) + vnfr_msg.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict()) + + vnfr_msg.create_time = self._create_time vnfr_msg.uptime = int(time.time()) - self._create_time vnfr_msg.mgmt_interface = mgmt_intf @@ -1375,6 +1378,17 @@ class VirtualNetworkFunctionRecord(object): return vnfr_msg + @asyncio.coroutine + def update_config(self, msg, xact): + self._log.debug("VNFM vnf config: {}". + format(msg.vnf_configuration.as_dict())) + self._config_status = msg.config_status + self._vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict( + msg.as_dict()) + self._log.debug("VNFR msg config: {}". + format(self.msg.vnf_configuration.as_dict())) + yield from self.publish(xact) + @property def dashboard_url(self): ip, cfg_port = self.mgmt_intf_info() @@ -2229,9 +2243,8 @@ class VnfrDtsHandler(object): self._log.debug("VNFR {} update config status {} (current {})". format(vnfr.name, msg.config_status, vnfr.config_status)) - # Update the config status and publish - vnfr._config_status = msg.config_status - yield from vnfr.publish(None) + # Update the config and publish + yield from vnfr.update_config(msg, xact_info) else: raise NotImplementedError( @@ -2251,7 +2264,6 @@ class VnfrDtsHandler(object): handler=hdl, flags=(rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | - rwdts.Flag.CACHE | rwdts.Flag.DATASTORE),) @asyncio.coroutine @@ -2267,13 +2279,13 @@ class VnfrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def update(self, xact, path, msg): + def update(self, xact, path, msg, flags=rwdts.XactFlag.REPLACE): """ Update a VNFR record in DTS with path and message """ self._log.debug("Updating VNFR xact = %s, %s:%s", xact, path, msg) - self.regh.update_element(path, msg) + self.regh.update_element(path, msg, flags) self._log.debug("Updated VNFR xact = %s, %s:%s", xact, path, msg) diff --git a/rwlaunchpad/test/mano_ut.py b/rwlaunchpad/test/mano_ut.py index 69a0d40e..a4ef59d2 100755 --- a/rwlaunchpad/test/mano_ut.py +++ b/rwlaunchpad/test/mano_ut.py @@ -642,7 +642,7 @@ class PingPongDescriptorPublisher(object): internal_vlr_count=num_internal_vlrs, num_vnf_vms=2, mano_ut=True, - use_scale_group=True, + use_scale_group=False, use_mon_params=False, ) -- 2.25.1