tacker/samples/mgmt_driver/ansible/ansible_driver.py

291 lines
10 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
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
# load the configuration file
config_yaml = self._load_ansible_config(request_obj)
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_stack_id"]
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):
# 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,
utils.CONFIG_FOLDER)
script_ansible_config = None
# load ScriptANSIBLE/config.yaml
if os.path.exists(script_ansible_path):
for file in os.listdir(script_ansible_path):
if file.endswith('yaml') and file.startswith('config'):
with open(
os.path.join(
script_ansible_path, file)) 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