![Pooja Singla](/assets/img/avatar_default.png)
The Ansible driver can only run specific script files. Due to multi artifact support of mgmt driver, it will be extended to specify script files to be executed in each life cycle. Implements: blueprint add-ansible-mgmt-driver-sample Change-Id: Id584a41fc7ae64a682d6dd390a38913e2c7e655b
319 lines
12 KiB
Python
319 lines
12 KiB
Python
# 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 os
|
|
import yaml
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
|
|
from tacker.vnfm.mgmt_drivers.ansible import ansible_config_parser
|
|
from tacker.vnfm.mgmt_drivers.ansible import config_validator
|
|
from tacker.vnfm.mgmt_drivers.ansible import event_handler
|
|
from tacker.vnfm.mgmt_drivers.ansible import exceptions
|
|
from tacker.vnfm.mgmt_drivers.ansible import heat_client
|
|
from tacker.vnfm.mgmt_drivers.ansible import utils
|
|
|
|
from tacker.vnfm.mgmt_drivers.ansible.config_actions.\
|
|
vm_app_config import vm_app_config
|
|
|
|
from tacker.vnflcm import utils as vnflcm_utils
|
|
from tacker.vnfm.mgmt_drivers import constants as mgmt_constants
|
|
from tacker.vnfm import plugin
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
EVENT_HANDLER = event_handler.AnsibleEventHandler()
|
|
SUPPORTED_ACTIONS = [
|
|
mgmt_constants.ACTION_INSTANTIATE_VNF,
|
|
mgmt_constants.ACTION_TERMINATE_VNF,
|
|
mgmt_constants.ACTION_HEAL_VNF,
|
|
mgmt_constants.ACTION_UPDATE_VNF,
|
|
mgmt_constants.ACTION_SCALE_IN_VNF,
|
|
mgmt_constants.ACTION_SCALE_OUT_VNF,
|
|
]
|
|
|
|
|
|
class AnsibleDriver(object):
|
|
|
|
def __init__(self):
|
|
self._config_queue = {}
|
|
self._has_error = False
|
|
self._cfg_parser = ansible_config_parser.ConfigParser()
|
|
self._cfg_validator = config_validator.AnsibleConfigValidator()
|
|
|
|
self._config_actions = {}
|
|
self._config_actions["vm_app_config"] = \
|
|
vm_app_config.VmAppConfigAction()
|
|
|
|
self._vnf = None
|
|
self._plugin = plugin.VNFMPlugin()
|
|
self._vnf_instance = None
|
|
self._context = None
|
|
|
|
LOG.debug("Ansible Driver initialized successfully!")
|
|
|
|
def get_type(self):
|
|
"""Mgmt driver for ansible"""
|
|
pass
|
|
|
|
def get_name(self):
|
|
"""Ansible Mgmt Driver"""
|
|
pass
|
|
|
|
def get_description(self):
|
|
pass
|
|
|
|
def _driver_process_flow(self, context, vnf_instance, action,
|
|
request_obj, **kwargs):
|
|
# set global, prevent passing for every function call
|
|
self._vnf = kwargs['vnf']
|
|
self._vnf_instance = vnf_instance
|
|
self._context = context
|
|
|
|
vnfd_dict = vnflcm_utils.get_vnfd_dict(context,
|
|
vnf_instance.vnfd_id,
|
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
|
vnf_value = vnfd_dict['topology_template']['node_templates']['VNF']
|
|
interfaces_vnflcm_value = (vnf_value.get('interfaces', {})
|
|
.get('Vnflcm', {}))
|
|
artifacts_vnflcm_value = vnf_value.get('artifacts', {})
|
|
|
|
start_msg = ("Ansible Management Driver invoked for configuration of"
|
|
"VNF: {}".format(self._vnf.get("name")))
|
|
|
|
insta_info = self._vnf_instance.instantiated_vnf_info
|
|
|
|
if action == mgmt_constants.ACTION_HEAL_VNF:
|
|
for vnfc_info in insta_info.vnfc_resource_info:
|
|
is_exist = [instance_id for instance_id in
|
|
request_obj.vnfc_instance_id
|
|
if instance_id == vnfc_info.id]
|
|
|
|
if (not len(request_obj.vnfc_instance_id)
|
|
or len(is_exist)):
|
|
self._vnf['failed_vdu_name'] = vnfc_info.vdu_id
|
|
break
|
|
|
|
EVENT_HANDLER.create_event(context, self._vnf,
|
|
utils.get_event_by_action(action,
|
|
self._vnf.get("failed_vdu_name", None)),
|
|
start_msg)
|
|
|
|
# get the mgmt_url
|
|
vnf_mgmt_ip_address = self._vnf.get("mgmt_ip_address", None)
|
|
if vnf_mgmt_ip_address is not None:
|
|
mgmt_url = jsonutils.loads(vnf_mgmt_ip_address)
|
|
LOG.debug("mgmt_url %s", mgmt_url)
|
|
else:
|
|
LOG.info("Unable to retrieve mgmt_ip_address of VNF")
|
|
return
|
|
|
|
action_interface = None
|
|
if action == mgmt_constants.ACTION_TERMINATE_VNF:
|
|
action_interface = 'terminate_start'
|
|
elif action == mgmt_constants.ACTION_SCALE_IN_VNF:
|
|
action_interface = 'scale_start'
|
|
elif action == mgmt_constants.ACTION_SCALE_OUT_VNF:
|
|
action_interface = 'scale_end'
|
|
elif action == mgmt_constants.ACTION_INSTANTIATE_VNF:
|
|
action_interface = 'instantiate_end'
|
|
elif action == mgmt_constants.ACTION_HEAL_VNF:
|
|
action_interface = 'heal_end'
|
|
|
|
action_value = interfaces_vnflcm_value.get(action_interface, {})
|
|
action_dependencies = (action_value.get('implementation', {})
|
|
.get('dependencies', []))
|
|
|
|
# NOTE: Currently action_dependencies is having the value of
|
|
# last element in the list because in the current specification
|
|
# only a single value is available for dependencies.
|
|
if isinstance(action_dependencies, list):
|
|
for arti in action_dependencies:
|
|
action_dependencies = arti
|
|
|
|
filename = artifacts_vnflcm_value.get(action_dependencies,
|
|
{}).get('file', {})
|
|
|
|
# load the configuration file
|
|
config_yaml = self._load_ansible_config(request_obj, filename)
|
|
if not config_yaml:
|
|
return
|
|
|
|
# validate config file
|
|
self._cfg_validator.validate(config_yaml)
|
|
|
|
# filter VDUs
|
|
if (action != mgmt_constants.ACTION_SCALE_IN_VNF and
|
|
action != mgmt_constants.ACTION_SCALE_OUT_VNF):
|
|
config_yaml = self._cfg_validator.filter_vdus(context, self._vnf,
|
|
utils.get_event_by_action(action,
|
|
self._vnf.get("failed_vdu_name", None)), mgmt_url,
|
|
config_yaml)
|
|
|
|
# load stack map
|
|
stack_map = self._get_stack_map(action, **kwargs)
|
|
|
|
# configure config parser for vnf parameter passing
|
|
self._cfg_parser.configure(self._context, self._vnf, self._plugin,
|
|
config_yaml, stack_map)
|
|
|
|
self._sort_config(config_yaml)
|
|
|
|
self._process_config(stack_map, config_yaml, mgmt_url, action)
|
|
|
|
def _sort_config(self, config_yaml):
|
|
self._config_queue = {}
|
|
for vdu, vdu_dict in config_yaml.get("vdus", {}).items():
|
|
self._add_to_config_queue(vdu, vdu_dict.get("config", {}))
|
|
|
|
def _process_config(self, stack_map, config_yaml, mgmt_url, action):
|
|
for vdu_order in sorted(self._config_queue):
|
|
config_info_list = self._config_queue[vdu_order]
|
|
for config_info in config_info_list:
|
|
vdu = config_info["vdu"]
|
|
config = config_info["config"]
|
|
|
|
for key, conf_value in config.items():
|
|
if key not in self._config_actions:
|
|
continue
|
|
|
|
LOG.debug("Processing configuration: {}".format(key))
|
|
self._config_actions[key].execute(
|
|
vdu=vdu,
|
|
vnf=self._vnf,
|
|
context=self._context,
|
|
conf_value=conf_value,
|
|
mgmt_url=mgmt_url,
|
|
cfg_parser=self._cfg_parser,
|
|
stack_map=stack_map,
|
|
config_yaml=config_yaml,
|
|
action=action,
|
|
)
|
|
|
|
def _add_to_config_queue(self, vdu, config):
|
|
if "order" not in config:
|
|
raise exceptions.MandatoryKeyNotDefinedError(vdu=vdu, key="order")
|
|
|
|
try:
|
|
order = int(config["order"])
|
|
except ValueError:
|
|
raise exceptions.InvalidValueError(vdu=vdu, key="order")
|
|
|
|
config_info = {
|
|
"vdu": vdu,
|
|
"config": config
|
|
}
|
|
|
|
if order in self._config_queue:
|
|
self._config_queue[order].append(config_info)
|
|
else:
|
|
entity_list = []
|
|
entity_list.append(config_info)
|
|
self._config_queue[order] = entity_list
|
|
|
|
def _get_stack_map(self, action, **kwargs):
|
|
stack_id_map = {}
|
|
stack_id = None
|
|
stack_id_list = None
|
|
scaling_actions = [
|
|
mgmt_constants.ACTION_SCALE_IN_VNF,
|
|
mgmt_constants.ACTION_SCALE_OUT_VNF,
|
|
]
|
|
|
|
if action in scaling_actions:
|
|
stack_id = kwargs["scale_out_id_list"]
|
|
else:
|
|
stack_id = self._vnf_instance.instantiated_vnf_info.instance_id
|
|
|
|
LOG.debug("stack_id: {}".format(stack_id))
|
|
if stack_id:
|
|
if not isinstance(stack_id, list):
|
|
stack_id_list = [stack_id]
|
|
else:
|
|
stack_id_list = stack_id
|
|
|
|
hc = heat_client.AnsibleHeatClient(self._context, self._plugin,
|
|
self._vnf)
|
|
|
|
for stack_ids in stack_id_list:
|
|
parent_stack_id = hc.get_parent_stack_id(stack_ids)
|
|
for resource in hc.get_resource_list(parent_stack_id):
|
|
if resource.physical_resource_id == stack_ids:
|
|
attr = hc.get_resource_attributes(
|
|
parent_stack_id, resource.resource_name)
|
|
stack_id_map = self._add_to_stack_map(stack_id_map, attr)
|
|
|
|
LOG.debug("stack_id_map: {}".format(stack_id_map))
|
|
return stack_id_map
|
|
|
|
def _add_to_stack_map(self, map, attributes):
|
|
for key, value in attributes.items():
|
|
if "mgmt_ip-" in key:
|
|
vdu_name = key.replace("mgmt_ip-", "")
|
|
if vdu_name in map:
|
|
map[vdu_name].append(value)
|
|
else:
|
|
map[vdu_name] = [value]
|
|
return map
|
|
|
|
def _get_config(self, config_data, config_params, vnf_package_path):
|
|
configurable_properties = config_data.get('configurable_properties')
|
|
|
|
# add vnf_package_path to vnf_configurable properties
|
|
if not configurable_properties:
|
|
configurable_properties = {
|
|
'_VAR_vnf_package_path': vnf_package_path + '/'
|
|
}
|
|
else:
|
|
configurable_properties.update(
|
|
{'_VAR_vnf_package_path': vnf_package_path + '/'})
|
|
|
|
for k, v in config_params.items():
|
|
var_key = '_VAR_' + k
|
|
configurable_properties.update({var_key: v})
|
|
|
|
config_data.update(
|
|
{'configurable_properties': configurable_properties})
|
|
|
|
LOG.debug('Modified config {}'.format(config_data))
|
|
|
|
return yaml.dump(config_data)
|
|
|
|
def _load_ansible_config(self, request_obj, filename):
|
|
# load vnf package path
|
|
vnf_package_path = vnflcm_utils._get_vnf_package_path(self._context,
|
|
self._vnf_instance.vnfd_id)
|
|
script_ansible_path = os.path.join(vnf_package_path, filename)
|
|
|
|
script_ansible_config = None
|
|
# load ScriptANSIBLE/config.yaml
|
|
if os.path.exists(script_ansible_path):
|
|
with open(script_ansible_path) as file_obj:
|
|
script_ansible_config = yaml.safe_load(file_obj)
|
|
|
|
if script_ansible_config is None:
|
|
LOG.error("not defined ansible script config")
|
|
|
|
config_params = {}
|
|
|
|
if hasattr(self._vnf_instance.instantiated_vnf_info,
|
|
'additional_params'):
|
|
config_params = self._vnf_instance.instantiated_vnf_info.\
|
|
additional_params
|
|
elif hasattr(request_obj, 'additional_params'):
|
|
config_params = request_obj.additional_params
|
|
|
|
self._vnf['attributes']['config'] = self._get_config(
|
|
script_ansible_config, config_params, vnf_package_path)
|
|
|
|
return script_ansible_config
|