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'])

        image_name = self._get_image_name(device_data)
        if image_name:
            self._update_vendor_data(device_data, device_data.get('token'))
        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
            try:
                self.compute_handler_nova.delete_instance(
                    token, self._get_admin_tenant_id(token=token),
                    device_data['id'])
            except Exception:
                LOG.error(_LE('Failed to delete %(instance)s instance'), {
                    'instance':
                    device_data['service_details']['device_type']
                })
        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(
                    _LE('Failed to delete the management data port(s). '
                        'Error: %(error)s'), {'error': e})
    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_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

        try:
            device = self.compute_handler_nova.get_instance(
                device_data['token'],
                device_data['tenant_id'],
                device_data['id'])
        except Exception:
            if ignore_failure:
                return None
            LOG.error(_LE('Failed to get %(instance)s instance details'),
                {'instance': device_data['service_details']['device_type']})
            return None  # TODO(RPM): should we raise an Exception here?

        return device['status']
    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'])

        image_name = self._get_image_name(device_data)
        if image_name:
            self._update_vendor_data(device_data, device_data.get('token'))

        token = self._get_token(device_data.get('token'))
        if not token:
            return None

        try:
            for port in device_data['ports']:
                port_id = network_handler.get_port_id(token, port['id'])
                self.compute_handler_nova.detach_interface(
                    token, self._get_admin_tenant_id(token=token),
                    device_data['id'], port_id)

        except Exception as e:
            LOG.error(
                _LE('Failed to unplug interface(s) from the device.'
                    'Error: %(error)s'), {'error': e})
            return None
        else:
            return True
    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 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']
    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
    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
        """
        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'])

        token = device_data['token']
        admin_tenant_id = device_data['admin_tenant_id']
        image_name = self._get_image_name(device_data)

        executor = nfp_executor.TaskExecutor(jobs=3)

        image_id_result = {}

        executor.add_job('UPDATE_VENDOR_DATA', self._update_vendor_data_fast,
                         token, admin_tenant_id, image_name, device_data)
        executor.add_job('GET_INTERFACES_FOR_DEVICE_CREATE',
                         self._get_interfaces_for_device_create, token,
                         admin_tenant_id, network_handler, device_data)
        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)

        executor.fire()

        interfaces = device_data.pop('interfaces', None)
        if not interfaces:
            LOG.exception(_LE('Failed to get interfaces for device creation.'))
            return None
        else:
            management_interface = interfaces[0]

        image_id = image_id_result.get('result', None)
        if not image_id:
            LOG.error(_LE('Failed to get image id for device creation.'))
            self._delete_interfaces(device_data,
                                    interfaces,
                                    network_handler=network_handler)
            return None

        if device_data['service_details'].get('flavor'):
            flavor = device_data['service_details']['flavor']
        else:
            LOG.info(
                _LI("No Device flavor provided in service profile's "
                    "service flavor field, using default "
                    "flavor: m1.medium"))
            flavor = 'm1.medium'

        interfaces_to_attach = []
        for interface in interfaces:
            interfaces_to_attach.append({'port': interface['port_id']})

        instance_name = device_data['name']
        instance_id_result = {}
        port_details_result = {}

        executor.add_job('CREATE_INSTANCE',
                         self.create_instance,
                         self.compute_handler_nova,
                         token,
                         admin_tenant_id,
                         image_id,
                         flavor,
                         interfaces_to_attach,
                         instance_name,
                         result_store=instance_id_result)

        executor.add_job('GET_NEUTRON_PORT_DETAILS',
                         self.get_neutron_port_details,
                         network_handler,
                         token,
                         management_interface['port_id'],
                         result_store=port_details_result)

        executor.fire()

        instance_id = instance_id_result.get('result', None)
        if not instance_id:
            LOG.error(_LE('Failed to create %(device_type)s instance.'))
            self._delete_interfaces(device_data,
                                    interfaces,
                                    network_handler=network_handler)
            return None

        mgmt_ip_address = None
        mgmt_neutron_port_info = port_details_result.get('result', None)

        if not mgmt_neutron_port_info:
            LOG.error(_LE('Failed to get management port details. '))
            try:
                self.compute_handler_nova.delete_instance(
                    token, admin_tenant_id, instance_id)
            except Exception as e:
                LOG.error(
                    _LE('Failed to delete %(device_type)s instance.'
                        'Error: %(error)s'), {
                            'device_type':
                            (device_data['service_details']['device_type']),
                            'error':
                            e
                        })
            self._delete_interfaces(device_data,
                                    interfaces,
                                    network_handler=network_handler)
            return None

        mgmt_ip_address = mgmt_neutron_port_info['ip_address']
        return {
            'id': instance_id,
            'name': instance_name,
            '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