1071 lines
42 KiB
Python
1071 lines
42 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 ast
|
|
from collections import defaultdict
|
|
|
|
from gbpservice.nfp.common import constants as nfp_constants
|
|
from gbpservice.nfp.common import data_formatter as df
|
|
from gbpservice.nfp.common import exceptions
|
|
from gbpservice.nfp.core import executor as nfp_executor
|
|
from gbpservice.nfp.core import log as nfp_logging
|
|
from gbpservice.nfp.lib import nfp_context_manager as nfp_ctx_mgr
|
|
from gbpservice.nfp.orchestrator.coal.networking import (
|
|
nfp_gbp_network_driver
|
|
)
|
|
from gbpservice.nfp.orchestrator.coal.networking import (
|
|
nfp_neutron_network_driver
|
|
)
|
|
from gbpservice.nfp.orchestrator.openstack import openstack_driver
|
|
|
|
LOG = nfp_logging.getLogger(__name__)
|
|
|
|
|
|
def _set_network_handler(f):
|
|
def wrapped(self, *args, **kwargs):
|
|
if type(args[0]) == dict:
|
|
device_data = args[0]
|
|
else:
|
|
device_data = args[1]
|
|
if device_data.get('service_details'):
|
|
network_mode = device_data['service_details'].get('network_mode')
|
|
if network_mode:
|
|
kwargs['network_handler'] = self.network_handlers[network_mode]
|
|
return f(self, *args, **kwargs)
|
|
return wrapped
|
|
|
|
|
|
class OrchestrationDriver(object):
|
|
"""Generic Driver class for orchestration of virtual appliances
|
|
|
|
Launches the VM with all the management and data ports and a new VM
|
|
is launched for each Network Service Instance
|
|
"""
|
|
|
|
def __init__(self, config, supports_device_sharing=False,
|
|
supports_hotplug=False, max_interfaces=8):
|
|
self.service_vendor = 'general'
|
|
self.supports_device_sharing = supports_device_sharing
|
|
self.supports_hotplug = supports_hotplug
|
|
self.maximum_interfaces = max_interfaces
|
|
self.identity_handler = openstack_driver.KeystoneClient(config)
|
|
self.compute_handler_nova = openstack_driver.NovaClient(config)
|
|
self.network_handlers = {
|
|
nfp_constants.GBP_MODE:
|
|
nfp_gbp_network_driver.NFPGBPNetworkDriver(config),
|
|
nfp_constants.NEUTRON_MODE:
|
|
nfp_neutron_network_driver.NFPNeutronNetworkDriver(config)
|
|
}
|
|
self.config = config
|
|
|
|
def _get_admin_tenant_id(self, token=None):
|
|
with nfp_ctx_mgr.KeystoneContextManager as kcm:
|
|
if not token:
|
|
token = kcm.retry(
|
|
self.identity_handler.get_admin_token, tries=3)
|
|
admin_tenant_name = (
|
|
self.config.nfp_keystone_authtoken.admin_tenant_name)
|
|
admin_tenant_id = kcm.retry(self.identity_handler.get_tenant_id,
|
|
token,
|
|
admin_tenant_name, tries=3)
|
|
return admin_tenant_id
|
|
|
|
def _get_token(self, device_data_token):
|
|
|
|
with nfp_ctx_mgr.KeystoneContextManager as kcm:
|
|
token = (device_data_token
|
|
if device_data_token
|
|
else kcm.retry(
|
|
self.identity_handler.get_admin_token, tries=3))
|
|
return token
|
|
|
|
def _is_device_sharing_supported(self):
|
|
return False
|
|
|
|
def _create_management_interface(self, token, admin_tenant_id,
|
|
device_data, network_handler):
|
|
|
|
name = nfp_constants.MANAGEMENT_INTERFACE_NAME
|
|
mgmt_interface = network_handler.create_port(
|
|
token,
|
|
admin_tenant_id,
|
|
device_data['management_network_info']['id'],
|
|
name=name)
|
|
|
|
return {'id': mgmt_interface['id'],
|
|
'port_id': mgmt_interface['port_id'],
|
|
'port_model': (nfp_constants.GBP_PORT
|
|
if device_data['service_details'][
|
|
'network_mode'] ==
|
|
nfp_constants.GBP_MODE
|
|
else nfp_constants.NEUTRON_PORT),
|
|
'port_classification': nfp_constants.MANAGEMENT,
|
|
'port_role': None}
|
|
|
|
def _delete_interface(self, device_data, interface,
|
|
network_handler=None):
|
|
token = self._get_token(device_data.get('token'))
|
|
if not token:
|
|
return None
|
|
|
|
network_handler.delete_port(token, interface['id'])
|
|
|
|
def _get_interfaces_for_device_create(self, token, admin_tenant_id,
|
|
network_handler, device_data):
|
|
try:
|
|
mgmt_interface = self._create_management_interface(
|
|
token,
|
|
admin_tenant_id,
|
|
device_data,
|
|
network_handler)
|
|
device_data['interfaces'] = [mgmt_interface]
|
|
except Exception as e:
|
|
LOG.exception('Failed to get interfaces for device creation.'
|
|
'Error: %(error)s', {'error': e})
|
|
|
|
def _delete_interfaces(self, device_data, interfaces,
|
|
network_handler=None):
|
|
for interface in interfaces:
|
|
self._delete_interface(
|
|
device_data, interface,
|
|
network_handler=network_handler)
|
|
|
|
def _verify_provider_metadata(self, image_name, metadata):
|
|
provider_metadata = {}
|
|
try:
|
|
for attr in metadata:
|
|
if attr in nfp_constants.METADATA_SUPPORTED_ATTRIBUTES:
|
|
provider_metadata[attr] = ast.literal_eval(metadata[attr])
|
|
except Exception as e:
|
|
LOG.error('Wrong metadata: %(metadata)s provided for '
|
|
'image name: %(image_name)s. Error: %(error)s',
|
|
{'image_name': image_name, 'metadata': metadata,
|
|
'error': e})
|
|
return None
|
|
return provider_metadata
|
|
|
|
def _get_provider_metadata(self, device_data, image_name):
|
|
token = self._get_token(device_data.get('token'))
|
|
if not token:
|
|
return None
|
|
with nfp_ctx_mgr.NovaContextManager as ncm:
|
|
metadata = ncm.retry(self.compute_handler_nova.get_image_metadata,
|
|
token,
|
|
self._get_admin_tenant_id(token=token),
|
|
image_name)
|
|
provider_metadata = self._verify_provider_metadata(
|
|
image_name, metadata)
|
|
if not provider_metadata:
|
|
return {}
|
|
return provider_metadata
|
|
|
|
def _get_provider_metadata_fast(self, token,
|
|
admin_tenant_id, image_name, device_data):
|
|
with nfp_ctx_mgr.NovaContextManager as ncm:
|
|
metadata = ncm.retry(self.compute_handler_nova.get_image_metadata,
|
|
token,
|
|
admin_tenant_id,
|
|
image_name)
|
|
provider_metadata = self._verify_provider_metadata(
|
|
image_name, metadata)
|
|
if not provider_metadata:
|
|
return {}
|
|
return provider_metadata
|
|
|
|
def _update_self_with_provider_metadata(self, provider_metadata, attr):
|
|
attr_value = getattr(self, attr)
|
|
if attr in provider_metadata:
|
|
setattr(self, attr, provider_metadata[attr])
|
|
else:
|
|
LOG.debug("Provider metadata specified in image, doesn't contains "
|
|
"%s value, proceeding with default value "
|
|
"%s", (attr, attr_value))
|
|
|
|
def _update_provider_metadata(self, device_data, token=None):
|
|
provider_metadata = {}
|
|
try:
|
|
image_name = self._get_image_name(device_data)
|
|
provider_metadata = self._get_provider_metadata(device_data,
|
|
image_name)
|
|
LOG.debug("Provider metadata, specified in image: %s",
|
|
provider_metadata)
|
|
if provider_metadata:
|
|
self._update_self_with_provider_metadata(
|
|
provider_metadata,
|
|
nfp_constants.MAXIMUM_INTERFACES)
|
|
self._update_self_with_provider_metadata(
|
|
provider_metadata,
|
|
nfp_constants.SUPPORTS_HOTPLUG)
|
|
else:
|
|
LOG.debug("No provider metadata specified in image,"
|
|
" proceeding with default values")
|
|
except Exception:
|
|
LOG.error("Error while getting metadata for image name:"
|
|
"%(image_name)s, proceeding with default values",
|
|
{'image_name': image_name})
|
|
return provider_metadata
|
|
|
|
def _update_provider_metadata_fast(self, token, admin_tenant_id,
|
|
image_name, device_data):
|
|
provider_metadata = None
|
|
try:
|
|
provider_metadata = self._get_provider_metadata_fast(
|
|
token, admin_tenant_id, image_name, device_data)
|
|
LOG.debug("Provider metadata, specified in image: %s",
|
|
provider_metadata)
|
|
if provider_metadata:
|
|
self._update_self_with_provider_metadata(
|
|
provider_metadata,
|
|
nfp_constants.MAXIMUM_INTERFACES)
|
|
self._update_self_with_provider_metadata(
|
|
provider_metadata,
|
|
nfp_constants.SUPPORTS_HOTPLUG)
|
|
else:
|
|
LOG.debug("No provider metadata specified in image,"
|
|
" proceeding with default values")
|
|
except Exception:
|
|
LOG.error("Error while getting metadata for image name: "
|
|
"%(image_name)s, proceeding with default values",
|
|
{'image_name': image_name})
|
|
return provider_metadata
|
|
|
|
def _get_image_name(self, device_data):
|
|
if device_data['service_details'].get('image_name'):
|
|
image_name = device_data['service_details']['image_name']
|
|
else:
|
|
LOG.debug("No image name provided in service profile's "
|
|
"service flavor field, image will be selected "
|
|
"based on service vendor's name : %s",
|
|
(device_data['service_details']['service_vendor']))
|
|
image_name = device_data['service_details']['service_vendor']
|
|
image_name = '%s' % image_name.lower()
|
|
device_data['service_details']['image_name'] = image_name
|
|
return image_name
|
|
|
|
def _get_service_type(self, token, service_profile_id, network_handler):
|
|
service_profile = network_handler.get_service_profile(
|
|
token, service_profile_id)
|
|
return service_profile['service_type']
|
|
|
|
def _get_device_service_types_map(self, token, devices, network_handler):
|
|
device_service_types_map = defaultdict(set)
|
|
for device in devices:
|
|
for network_function in device['network_functions']:
|
|
service_type = self._get_service_type(
|
|
token,
|
|
network_function['service_profile_id'],
|
|
network_handler)
|
|
device_service_types_map[device['id']].add(service_type)
|
|
return device_service_types_map
|
|
|
|
def get_image_id(self, nova, token, admin_tenant_id, image_name):
|
|
try:
|
|
image_id = nova.get_image_id(token, admin_tenant_id, image_name)
|
|
return image_id
|
|
except Exception as e:
|
|
LOG.error('Failed to get image id for device creation.'
|
|
' image name: %(image_name)s. Error: %(error)s',
|
|
{'image_name': image_name, 'error': e})
|
|
|
|
def create_instance(self, nova, token, admin_tenant_id,
|
|
image_id, flavor, interfaces_to_attach,
|
|
instance_name, volume_support,
|
|
volume_size, files=None, user_data=None,
|
|
server_grp_id=None):
|
|
try:
|
|
instance_id = nova.create_instance(
|
|
token, admin_tenant_id, image_id, flavor,
|
|
interfaces_to_attach, instance_name, volume_support,
|
|
volume_size, files=files, userdata=user_data,
|
|
server_grp_id=server_grp_id)
|
|
return instance_id
|
|
except Exception as e:
|
|
LOG.error('Failed to create instance.'
|
|
'Error: %(error)s', {'error': e})
|
|
|
|
def get_neutron_port_details(self, network_handler, token, port_id):
|
|
try:
|
|
(mgmt_ip_address,
|
|
mgmt_mac, mgmt_cidr, gateway_ip,
|
|
mgmt_port, mgmt_subnet) = (
|
|
network_handler.get_neutron_port_details(token, port_id))
|
|
|
|
result = {'neutron_port': mgmt_port['port'],
|
|
'neutron_subnet': mgmt_subnet['subnet'],
|
|
'ip_address': mgmt_ip_address,
|
|
'mac': mgmt_mac,
|
|
'cidr': mgmt_cidr,
|
|
'gateway_ip': gateway_ip}
|
|
return result
|
|
except Exception as e:
|
|
import sys
|
|
import traceback
|
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
LOG.error(traceback.format_exception(exc_type, exc_value,
|
|
exc_traceback))
|
|
LOG.error('Failed to get management port details. '
|
|
'Error: %(error)s', {'error': e})
|
|
|
|
@_set_network_handler
|
|
def create_network_function_device(self, device_data,
|
|
network_handler=None):
|
|
""" Create a NFD
|
|
|
|
:param device_data: NFD data
|
|
:type device_data: dict
|
|
|
|
:returns: None -- when there is a failure in creating NFD
|
|
:return: dict -- NFD created
|
|
|
|
:raises: exceptions.IncompleteData,
|
|
exceptions.ComputePolicyNotSupported
|
|
"""
|
|
self._validate_create_nfd_data(device_data)
|
|
|
|
token = device_data['token']
|
|
admin_tenant_id = device_data['admin_tenant_id']
|
|
image_name = self._get_image_name(device_data)
|
|
|
|
pre_launch_executor = nfp_executor.TaskExecutor(jobs=3)
|
|
|
|
image_id_result = {}
|
|
provider_metadata_result = {}
|
|
|
|
pre_launch_executor.add_job('UPDATE_PROVIDER_METADATA',
|
|
self._update_provider_metadata_fast,
|
|
token, admin_tenant_id,
|
|
image_name, device_data,
|
|
result_store=provider_metadata_result)
|
|
pre_launch_executor.add_job('GET_INTERFACES_FOR_DEVICE_CREATE',
|
|
self._get_interfaces_for_device_create,
|
|
token, admin_tenant_id,
|
|
network_handler, device_data)
|
|
pre_launch_executor.add_job('GET_IMAGE_ID',
|
|
self.get_image_id,
|
|
self.compute_handler_nova, token,
|
|
admin_tenant_id,
|
|
image_name, result_store=image_id_result)
|
|
|
|
pre_launch_executor.fire()
|
|
|
|
interfaces, image_id, provider_metadata = (
|
|
self._validate_pre_launch_executor_results(
|
|
network_handler,
|
|
device_data,
|
|
image_name,
|
|
image_id_result,
|
|
provider_metadata_result))
|
|
if not interfaces:
|
|
return None
|
|
|
|
management_interface = interfaces[0]
|
|
flavor = self._get_service_instance_flavor(device_data)
|
|
|
|
interfaces_to_attach = []
|
|
try:
|
|
for interface in interfaces:
|
|
interfaces_to_attach.append({'port': interface['port_id']})
|
|
if provider_metadata.get('supports_hotplug') is False:
|
|
self._update_interfaces_for_non_hotplug_support(
|
|
network_handler,
|
|
interfaces,
|
|
interfaces_to_attach,
|
|
device_data)
|
|
except Exception as e:
|
|
LOG.error('Failed to fetch list of interfaces to attach'
|
|
' for device creation %(error)s', {'error': e})
|
|
self._delete_interfaces(device_data, interfaces,
|
|
network_handler=network_handler)
|
|
return None
|
|
|
|
instance_name = device_data['name']
|
|
|
|
create_instance_executor = nfp_executor.TaskExecutor(jobs=3)
|
|
instance_id_result = {}
|
|
port_details_result = {}
|
|
volume_support = device_data['volume_support']
|
|
volume_size = device_data['volume_size']
|
|
create_instance_executor.add_job(
|
|
'CREATE_INSTANCE', self.create_instance,
|
|
self.compute_handler_nova, token,
|
|
admin_tenant_id, image_id, flavor,
|
|
interfaces_to_attach, instance_name,
|
|
volume_support, volume_size,
|
|
files=device_data.get('files'),
|
|
user_data=device_data.get('user_data'),
|
|
result_store=instance_id_result)
|
|
|
|
create_instance_executor.add_job(
|
|
'GET_NEUTRON_PORT_DETAILS',
|
|
self.get_neutron_port_details,
|
|
network_handler, token,
|
|
management_interface['port_id'],
|
|
result_store=port_details_result)
|
|
|
|
create_instance_executor.fire()
|
|
|
|
instance_id, mgmt_neutron_port_info = (
|
|
self._validate_create_instance_executor_results(
|
|
network_handler,
|
|
device_data,
|
|
interfaces,
|
|
instance_id_result,
|
|
port_details_result))
|
|
if not instance_id:
|
|
return None
|
|
|
|
mgmt_ip_address = mgmt_neutron_port_info['ip_address']
|
|
return {'id': instance_id,
|
|
'name': instance_name,
|
|
'provider_metadata': provider_metadata,
|
|
'mgmt_ip_address': mgmt_ip_address,
|
|
'mgmt_port_id': interfaces[0],
|
|
'mgmt_neutron_port_info': mgmt_neutron_port_info,
|
|
'max_interfaces': self.maximum_interfaces,
|
|
'interfaces_in_use': len(interfaces_to_attach),
|
|
'description': ''} # TODO(RPM): what should be the description
|
|
|
|
def _validate_create_nfd_data(self, device_data):
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['service_details',
|
|
'name',
|
|
'management_network_info',
|
|
'ports']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode']) or
|
|
|
|
any(key not in device_data['management_network_info']
|
|
for key in ['id']) or
|
|
|
|
type(device_data['ports']) is not list or
|
|
|
|
any(key not in port
|
|
for port in device_data['ports']
|
|
for key in ['id',
|
|
'port_classification',
|
|
'port_model'])
|
|
):
|
|
raise exceptions.IncompleteData()
|
|
|
|
if (
|
|
device_data['service_details']['device_type'] !=
|
|
nfp_constants.NOVA_MODE
|
|
):
|
|
raise exceptions.ComputePolicyNotSupported(
|
|
compute_policy=device_data['service_details']['device_type'])
|
|
|
|
def _validate_pre_launch_executor_results(self, network_handler,
|
|
device_data,
|
|
image_name,
|
|
image_id_result,
|
|
provider_metadata_result,
|
|
server_grp_id_result=None):
|
|
interfaces = device_data.pop('interfaces', None)
|
|
if not interfaces:
|
|
LOG.exception('Failed to get interfaces for device creation.')
|
|
return None, _, _
|
|
|
|
image_id = image_id_result.get('result', None)
|
|
if not image_id:
|
|
LOG.error('Failed to get image id for device creation.')
|
|
self._delete_interfaces(device_data, interfaces,
|
|
network_handler=network_handler)
|
|
return None, _, _
|
|
|
|
if server_grp_id_result and not server_grp_id_result.get('result'):
|
|
LOG.error('Validation failed for Nova anti-affinity '
|
|
'server group.')
|
|
return None, _, _
|
|
|
|
provider_metadata = provider_metadata_result.get('result', None)
|
|
if not provider_metadata:
|
|
LOG.warning('Failed to get provider metadata for'
|
|
' device creation.')
|
|
provider_metadata = {}
|
|
|
|
return interfaces, image_id, provider_metadata
|
|
|
|
def _get_service_instance_flavor(self, device_data):
|
|
if device_data['service_details'].get('flavor'):
|
|
flavor = device_data['service_details']['flavor']
|
|
else:
|
|
LOG.debug("No Device flavor provided in service profile's "
|
|
"service flavor field, using default "
|
|
"flavor: m1.medium")
|
|
flavor = 'm1.medium'
|
|
return flavor
|
|
|
|
def _update_interfaces_for_non_hotplug_support(self, network_handler,
|
|
interfaces,
|
|
interfaces_to_attach,
|
|
device_data):
|
|
token = device_data['token']
|
|
enable_port_security = device_data.get('enable_port_security')
|
|
if not device_data['interfaces_to_attach']:
|
|
for port in device_data['ports']:
|
|
if (port['port_classification'] ==
|
|
nfp_constants.PROVIDER):
|
|
if (device_data['service_details'][
|
|
'service_type'].lower()
|
|
in [nfp_constants.FIREWALL.lower(),
|
|
nfp_constants.VPN.lower()]):
|
|
network_handler.set_promiscuos_mode(
|
|
token, port['id'], enable_port_security)
|
|
port_id = network_handler.get_port_id(
|
|
token, port['id'])
|
|
interfaces_to_attach.append({'port': port_id})
|
|
for port in device_data['ports']:
|
|
if (port['port_classification'] ==
|
|
nfp_constants.CONSUMER):
|
|
if (device_data['service_details'][
|
|
'service_type'].lower()
|
|
in [nfp_constants.FIREWALL.lower(),
|
|
nfp_constants.VPN.lower()]):
|
|
network_handler.set_promiscuos_mode(
|
|
token, port['id'], enable_port_security)
|
|
port_id = network_handler.get_port_id(
|
|
token, port['id'])
|
|
interfaces_to_attach.append({'port': port_id})
|
|
else:
|
|
for interface in device_data['interfaces_to_attach']:
|
|
interfaces_to_attach.append(
|
|
{'port': interface['port']})
|
|
interfaces.append({'id': interface['id']})
|
|
|
|
def _validate_create_instance_executor_results(self,
|
|
network_handler,
|
|
device_data,
|
|
interfaces,
|
|
instance_id_result,
|
|
port_details_result):
|
|
token = device_data['token']
|
|
admin_tenant_id = device_data['admin_tenant_id']
|
|
instance_id = instance_id_result.get('result', None)
|
|
if not instance_id:
|
|
LOG.error('Failed to create instance with device data:'
|
|
'%(data)s.',
|
|
{'data': device_data})
|
|
self._delete_interfaces(device_data, interfaces,
|
|
network_handler=network_handler)
|
|
return None, _
|
|
|
|
mgmt_neutron_port_info = port_details_result.get('result', None)
|
|
|
|
if not mgmt_neutron_port_info:
|
|
LOG.error('Failed to get management port details. ')
|
|
with nfp_ctx_mgr.NovaContextManager as ncm:
|
|
ncm.retry(self.compute_handler_nova.delete_instance,
|
|
token,
|
|
admin_tenant_id,
|
|
instance_id)
|
|
self._delete_interfaces(device_data, interfaces,
|
|
network_handler=network_handler)
|
|
return None, _
|
|
return instance_id, mgmt_neutron_port_info
|
|
|
|
@_set_network_handler
|
|
def delete_network_function_device(self, device_data,
|
|
network_handler=None):
|
|
""" Delete the NFD
|
|
|
|
:param device_data: NFD
|
|
:type device_data: dict
|
|
|
|
:returns: None -- Both on success and Failure
|
|
|
|
:raises: exceptions.IncompleteData,
|
|
exceptions.ComputePolicyNotSupported
|
|
"""
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['service_details',
|
|
'mgmt_port_id']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode']) or
|
|
|
|
type(device_data['mgmt_port_id']) is not dict or
|
|
|
|
any(key not in device_data['mgmt_port_id']
|
|
for key in ['id',
|
|
'port_classification',
|
|
'port_model'])
|
|
):
|
|
raise exceptions.IncompleteData()
|
|
|
|
if (
|
|
device_data['service_details']['device_type'] !=
|
|
nfp_constants.NOVA_MODE
|
|
):
|
|
raise exceptions.ComputePolicyNotSupported(
|
|
compute_policy=device_data['service_details']['device_type'])
|
|
|
|
token = self._get_token(device_data.get('token'))
|
|
|
|
if not token:
|
|
return None
|
|
|
|
if device_data.get('id'):
|
|
# delete the device instance
|
|
#
|
|
# this method will be invoked again
|
|
# once the device instance deletion is completed
|
|
with nfp_ctx_mgr.NovaContextManager.new(
|
|
suppress=(Exception,)) as ncm:
|
|
|
|
ncm.retry(self.compute_handler_nova.delete_instance,
|
|
token,
|
|
device_data['tenant_id'],
|
|
device_data['id'])
|
|
else:
|
|
# device instance deletion is done, delete remaining resources
|
|
try:
|
|
interfaces = [device_data['mgmt_port_id']]
|
|
self._delete_interfaces(device_data,
|
|
interfaces,
|
|
network_handler=network_handler)
|
|
except Exception as e:
|
|
LOG.error('Failed to delete the management data port(s). '
|
|
'Error: %(error)s', {'error': e})
|
|
|
|
def get_network_function_device_status(self, device_data,
|
|
ignore_failure=False):
|
|
""" Get the status of NFD
|
|
|
|
:param device_data: NFD
|
|
:type device_data: dict
|
|
|
|
:returns: None -- On failure
|
|
:return: str -- status string
|
|
|
|
:raises: exceptions.IncompleteData,
|
|
exceptions.ComputePolicyNotSupported
|
|
"""
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['id',
|
|
'service_details']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode'])
|
|
):
|
|
raise exceptions.IncompleteData()
|
|
|
|
if (
|
|
device_data['service_details']['device_type'] !=
|
|
nfp_constants.NOVA_MODE
|
|
):
|
|
raise exceptions.ComputePolicyNotSupported(
|
|
compute_policy=device_data['service_details']['device_type'])
|
|
|
|
token = self._get_token(device_data.get('token'))
|
|
|
|
if not token:
|
|
return None
|
|
|
|
with nfp_ctx_mgr.NovaContextManager.new(suppress=(Exception,)) as ncm:
|
|
device = ncm.retry(self.compute_handler_nova.get_instance,
|
|
device_data['token'],
|
|
device_data['tenant_id'],
|
|
device_data['id'])
|
|
|
|
return device['status']
|
|
|
|
@_set_network_handler
|
|
def plug_network_function_device_interfaces(self, device_data,
|
|
network_handler=None):
|
|
""" Attach the network interfaces for NFD
|
|
|
|
:param device_data: NFD
|
|
:type device_data: dict
|
|
|
|
:returns: bool -- False on failure and True on Success
|
|
|
|
:raises: exceptions.IncompleteData,
|
|
exceptions.ComputePolicyNotSupported
|
|
"""
|
|
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['id',
|
|
'service_details',
|
|
'ports']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode']) or
|
|
|
|
type(device_data['ports']) is not list or
|
|
|
|
any(key not in port
|
|
for port in device_data['ports']
|
|
for key in ['id',
|
|
'port_classification',
|
|
'port_model'])
|
|
):
|
|
raise exceptions.IncompleteData()
|
|
|
|
if (
|
|
device_data['service_details']['device_type'] !=
|
|
nfp_constants.NOVA_MODE
|
|
):
|
|
raise exceptions.ComputePolicyNotSupported(
|
|
compute_policy=device_data['service_details']['device_type'])
|
|
|
|
token = device_data['token']
|
|
tenant_id = device_data['tenant_id']
|
|
provider_metadata = device_data['provider_metadata']
|
|
enable_port_security = device_data.get('enable_port_security')
|
|
|
|
if provider_metadata.get('supports_hotplug') is False:
|
|
return True
|
|
try:
|
|
executor = nfp_executor.TaskExecutor(jobs=10)
|
|
|
|
for port in device_data['ports']:
|
|
if port['port_classification'] == nfp_constants.PROVIDER:
|
|
service_type = device_data[
|
|
'service_details']['service_type'].lower()
|
|
if service_type.lower() in [
|
|
nfp_constants.FIREWALL.lower(),
|
|
nfp_constants.VPN.lower()]:
|
|
executor.add_job(
|
|
'SET_PROMISCUOS_MODE',
|
|
network_handler.set_promiscuos_mode_fast,
|
|
token, port['id'], enable_port_security)
|
|
executor.add_job(
|
|
'ATTACH_INTERFACE',
|
|
self.compute_handler_nova.attach_interface,
|
|
token, tenant_id, device_data['id'],
|
|
port['id'])
|
|
break
|
|
executor.fire()
|
|
|
|
for port in device_data['ports']:
|
|
if port['port_classification'] == nfp_constants.CONSUMER:
|
|
service_type = device_data[
|
|
'service_details']['service_type'].lower()
|
|
if service_type.lower() in [
|
|
nfp_constants.FIREWALL.lower(),
|
|
nfp_constants.VPN.lower()]:
|
|
executor.add_job(
|
|
'SET_PROMISCUOS_MODE',
|
|
network_handler.set_promiscuos_mode_fast,
|
|
token, port['id'], enable_port_security)
|
|
executor.add_job(
|
|
'ATTACH_INTERFACE',
|
|
self.compute_handler_nova.attach_interface,
|
|
token, tenant_id, device_data['id'],
|
|
port['id'])
|
|
break
|
|
executor.fire()
|
|
|
|
except Exception as e:
|
|
LOG.error('Failed to plug interface(s) to the device.'
|
|
'Error: %(error)s', {'error': e})
|
|
return None
|
|
else:
|
|
return True
|
|
|
|
@_set_network_handler
|
|
def unplug_network_function_device_interfaces(self, device_data,
|
|
network_handler=None):
|
|
""" Detach the network interfaces for NFD
|
|
|
|
:param device_data: NFD
|
|
:type device_data: dict
|
|
|
|
:returns: bool -- False on failure and True on Success
|
|
|
|
:raises: exceptions.IncompleteData,
|
|
exceptions.ComputePolicyNotSupported
|
|
"""
|
|
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['id',
|
|
'service_details',
|
|
'ports']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode']) or
|
|
|
|
any(key not in port
|
|
for port in device_data['ports']
|
|
for key in ['id',
|
|
'port_classification',
|
|
'port_model'])
|
|
):
|
|
raise exceptions.IncompleteData()
|
|
|
|
if (
|
|
device_data['service_details']['device_type'] !=
|
|
nfp_constants.NOVA_MODE
|
|
):
|
|
raise exceptions.ComputePolicyNotSupported(
|
|
compute_policy=device_data['service_details']['device_type'])
|
|
|
|
token = self._get_token(device_data.get('token'))
|
|
|
|
if not token:
|
|
return None
|
|
|
|
image_name = self._get_image_name(device_data)
|
|
provider_metadata = {}
|
|
if image_name:
|
|
provider_metadata = (
|
|
self._update_provider_metadata_fast(token,
|
|
device_data['tenant_id'],
|
|
image_name,
|
|
device_data))
|
|
|
|
if not provider_metadata:
|
|
LOG.debug('Failed to get provider metadata for'
|
|
' device deletion.')
|
|
|
|
if provider_metadata.get('supports_hotplug') is False:
|
|
return True
|
|
|
|
with nfp_ctx_mgr.NovaContextManager.new(suppress=(Exception,)) as ncm:
|
|
for port in device_data['ports']:
|
|
port_id = self._get_port_from_pt(device_data, port['id'])
|
|
ncm.retry(self.compute_handler_nova.detach_interface,
|
|
token,
|
|
device_data['tenant_id'],
|
|
device_data['id'],
|
|
port_id)
|
|
# Async change
|
|
self._delete_port(token, port_id)
|
|
# Async change: Delete stale l2ps
|
|
try:
|
|
self._delete_l2ps(token, device_data, network_handler)
|
|
except Exception:
|
|
pass
|
|
return True
|
|
|
|
def _delete_l2ps(self, token, device_data, network_handler):
|
|
'''
|
|
delete l2 policies
|
|
'''
|
|
gbp_cli = network_handler.network_handler
|
|
for ptg in device_data['provider']['ptg']:
|
|
try:
|
|
ptg_details = gbp_cli.get_policy_target_group(token,
|
|
ptg['id'])
|
|
if ptg_details:
|
|
LOG.debug('Provider ptg is in use !!')
|
|
continue
|
|
except Exception:
|
|
LOG.debug('Provider not found !!')
|
|
with nfp_ctx_mgr.GBPContextManager as gcm:
|
|
l2p = gbp_cli.get_l2_policy(token, ptg['l2_policy_id'])
|
|
# deleting l2p if it is created implicitly
|
|
if 'Implicitly' in l2p['description']:
|
|
gcm.retry(gbp_cli.delete_l2_policy, token,
|
|
ptg['l2_policy_id'])
|
|
|
|
if ('consumer' not in device_data.keys() or not device_data[
|
|
'consumer'].get('ptg')):
|
|
return
|
|
|
|
for ptg in device_data['consumer']['ptg']:
|
|
try:
|
|
ptg_details = gbp_cli.get_policy_target_group(token,
|
|
ptg['id'])
|
|
if ptg_details:
|
|
LOG.debug('Stitching PTG is in use !!')
|
|
continue
|
|
except Exception:
|
|
LOG.debug('Stitching PTG not found !!')
|
|
with nfp_ctx_mgr.GBPContextManager as gcm:
|
|
l2p = gbp_cli.get_l2_policy(token, ptg['l2_policy_id'])
|
|
# deleting l2p if it is created implicitly
|
|
if 'Implicitly' in l2p['description']:
|
|
gcm.retry(gbp_cli.delete_l2_policy, token,
|
|
ptg['l2_policy_id'])
|
|
|
|
def _delete_port(self, token, port_id):
|
|
'''
|
|
delete neutron port
|
|
'''
|
|
try:
|
|
network_handler = self.network_handlers[nfp_constants.NEUTRON_MODE]
|
|
network_handler.delete_port(token, port_id)
|
|
except Exception as exc:
|
|
LOG.error("Failed to delete port %(port_id)s. Error: %(exc)s",
|
|
{"port_id": port_id, 'exc': exc})
|
|
|
|
def _get_port_from_pt(self, device_data, pt_id):
|
|
'''
|
|
get neutron_port_id from pt_id using data
|
|
'''
|
|
port_id = None
|
|
for pt in device_data['provider']['pt']:
|
|
if pt['id'] == pt_id:
|
|
return pt['port_id']
|
|
|
|
if not device_data['consumer'].get('pt'):
|
|
return port_id
|
|
|
|
for pt in device_data['consumer']['pt']:
|
|
if pt['id'] == pt_id:
|
|
return pt['port_id']
|
|
LOG.error('Policy Target %(pt_id) not found in provided data',
|
|
{'pt_id': pt_id})
|
|
return port_id
|
|
|
|
def get_port_details(self, port_id, port_model, data):
|
|
'''
|
|
function to retrieve port details from data
|
|
'''
|
|
if 'gbp' in port_model:
|
|
for pt in data['pt']:
|
|
if port_id == pt['id']:
|
|
port_id = pt['port_id']
|
|
break
|
|
|
|
for port in data['port']:
|
|
if port_id == port['id']:
|
|
ip = port['fixed_ips'][0]['ip_address']
|
|
mac = port['mac_address']
|
|
cidr = data['subnet']['cidr']
|
|
gateway_ip = data['subnet']['gateway_ip']
|
|
return (ip, mac, cidr, gateway_ip, port, data['subnet'])
|
|
|
|
@_set_network_handler
|
|
def get_delete_device_data(self, device_data, network_handler=None,
|
|
devices_data=None):
|
|
""" Get the configuration information for NFD
|
|
|
|
:param device_data: NFD
|
|
:type device_data: dict
|
|
|
|
:param devices_data: metadata of NFD and NF
|
|
:type device_data: dict
|
|
|
|
:returns: None -- On Failure
|
|
:returns: dict
|
|
|
|
:raises: exceptions.IncompleteData
|
|
|
|
"""
|
|
|
|
if (
|
|
any(key not in device_data
|
|
for key in ['service_details',
|
|
'mgmt_ip_address',
|
|
'ports']) or
|
|
|
|
type(device_data['service_details']) is not dict or
|
|
|
|
any(key not in device_data['service_details']
|
|
for key in ['service_vendor',
|
|
'device_type',
|
|
'network_mode']) or
|
|
|
|
type(device_data['ports']) is not list or
|
|
|
|
any(key not in port
|
|
for port in device_data['ports']
|
|
for key in ['id',
|
|
'port_classification',
|
|
'port_model'])
|
|
):
|
|
LOG.error('Incomplete device data received for delete '
|
|
'network function device.')
|
|
return None
|
|
|
|
token = self._get_token(device_data.get('token'))
|
|
if not token:
|
|
return None
|
|
|
|
provider_ip = None
|
|
provider_mac = None
|
|
provider_cidr = None
|
|
consumer_ip = None
|
|
consumer_mac = None
|
|
consumer_cidr = None
|
|
consumer_gateway_ip = None
|
|
|
|
for port in device_data['ports']:
|
|
if port['port_classification'] == nfp_constants.PROVIDER:
|
|
try:
|
|
(provider_ip, provider_mac, provider_cidr, dummy, _, _) = (
|
|
self.get_port_details(port['id'], port['port_model'],
|
|
devices_data['provider'])
|
|
)
|
|
except Exception:
|
|
LOG.error('Failed to get provider port details'
|
|
' for get device config info operation')
|
|
return None
|
|
elif port['port_classification'] == nfp_constants.CONSUMER:
|
|
try:
|
|
(consumer_ip, consumer_mac, consumer_cidr,
|
|
consumer_gateway_ip, _, _) = (
|
|
self.get_port_details(port['id'], port['port_model'],
|
|
devices_data['consumer'])
|
|
)
|
|
except Exception:
|
|
LOG.error('Failed to get consumer port details'
|
|
' for get device config info operation')
|
|
return None
|
|
|
|
device_data.update({
|
|
'provider_ip': provider_ip, 'provider_mac': provider_mac,
|
|
'provider_cidr': provider_cidr, 'consumer_ip': consumer_ip,
|
|
'consumer_mac': consumer_mac, 'consumer_cidr': consumer_cidr,
|
|
'consumer_gateway_ip': consumer_gateway_ip})
|
|
|
|
return device_data
|
|
|
|
@_set_network_handler
|
|
def get_network_function_device_config(self, device_data,
|
|
resource_type, is_delete=False,
|
|
network_handler=None,
|
|
devices_data=None):
|
|
""" Get the configuration information for NFD
|
|
|
|
:returns: dict
|
|
|
|
"""
|
|
|
|
if is_delete:
|
|
device_data = self.get_delete_device_data(
|
|
device_data, network_handler=network_handler,
|
|
devices_data=devices_data)
|
|
if not device_data:
|
|
return None
|
|
|
|
return df.get_network_function_info(
|
|
device_data, resource_type)
|