def _get_amphorae_network_config(self, context, loadbalancer_dict,
                                     loadbalancer_o_obj):
        loadbalancer_n_obj = n_data_models.LoadBalancer.from_dict(
            copy.deepcopy(loadbalancer_dict))

        amphorae_network_config = {}

        for amp in loadbalancer_o_obj.amphorae:
            if amp.status != constants.DELETED:
                # Get vip_subnet
                vip_subnet = None
                for subnet_dict in context['service_info']['subnets']:
                    if subnet_dict['id'] == loadbalancer_n_obj.vip_subnet_id:
                        vip_subnet = n_data_models.Subnet.from_dict(
                            copy.deepcopy(subnet_dict))
                        break
                if vip_subnet is None:
                    raise exceptions.IncompleteData(
                        "VIP subnet information is not found")

                sc_metadata = ast.literal_eval(
                    loadbalancer_dict['description'])
                vrrp_port = n_data_models.Port(
                    mac_address=sc_metadata['provider_interface_mac'])
                if vrrp_port is None:
                    raise exceptions.IncompleteData(
                        "VRRP port information is not found")

                amphorae_network_config[amp.id] = \
                    network_data_models.AmphoraNetworkConfig(
                        amphora=amp,
                        vip_subnet=vip_subnet,
                        vrrp_port=vrrp_port)

        return amphorae_network_config
    def select_network_function_device(self,
                                       devices,
                                       device_data,
                                       network_handler=None):
        """ Select a NFD which is eligible for sharing

        :param devices: NFDs
        :type devices: list
        :param device_data: NFD data
        :type device_data: dict

        :returns: None -- when device sharing is not supported, or
                          when no device is eligible for sharing
        :return: dict -- NFD which is eligible for sharing

        :raises: exceptions.IncompleteData
        """

        if (any(key not in device_data
                for key in ['ports']) 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'])
                or type(devices) is not list
                or any(key not in device for device in devices
                       for key in ['interfaces_in_use'])):
            raise exceptions.IncompleteData()

        token = self._get_token(device_data.get('token'))
        if not token:
            return None
        image_name = self._get_image_name(device_data)
        if image_name:
            self._update_vendor_data(device_data, device_data.get('token'))
        if not self._is_device_sharing_supported():
            return None
    def get_network_function_device_sharing_info(self, device_data):
        """ Get filters for NFD sharing

        :param device_data: NFD data
        :type device_data: dict

        :returns: None -- when device sharing is not supported
        :returns: dict -- It has the following scheme
        {
            'filters': {
                'key': 'value',
                ...
            }
        }

        :raises: exceptions.IncompleteData
        """

        if (any(key not in device_data
                for key in ['tenant_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'])):
            raise exceptions.IncompleteData()

        if not self._is_device_sharing_supported():
            return None
    def get_network_function_device_healthcheck_info(self, device_data):
        """ Get the health check information for NFD

        :param device_data: NFD
        :type device_data: dict

        :returns: dict -- It has the following scheme
        {
            'config': [
                {
                    'resource': 'healthmonitor',
                    'resource_data': {
                        ...
                    }
                }
            ]
        }

        :raises: exceptions.IncompleteData
        """
        if (any(key not in device_data for key in ['id', 'mgmt_ip_address'])):
            raise exceptions.IncompleteData()

        return {
            'config': [{
                'resource': nfp_constants.HEALTHMONITOR_RESOURCE,
                'resource_data': {
                    'vmid': device_data['id'],
                    'mgmt_ip': device_data['mgmt_ip_address'],
                    'periodicity': 'initial'
                }
            }]
        }
    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 add_amphora(self, context, loadbalancer_id, description,
                 status=constants.ACTIVE):
     sc_metadata = ast.literal_eval(description)
     rdata = self.parse.parse_data(common_const.LOADBALANCERV2, context)
     if not (rdata['mgmt_ip'] and sc_metadata.get('network_function_id')):
         raise exceptions.IncompleteData(
             "Amphora information is missing")
     if not self.get_amphora(loadbalancer_id):
         # REVISIT(jiahao): use network_function_id as amphora id
         amp = o_data_models.Amphora(
             lb_network_ip=rdata['mgmt_ip'],
             id=sc_metadata['network_function_id'],
             status=status)
         self.amphorae[loadbalancer_id] = [amp]
 def add_amphora(self,
                 loadbalancer_id,
                 description,
                 status=constants.ACTIVE):
     sc_metadata = ast.literal_eval(description)
     if not (sc_metadata.get('floating_ip')
             and sc_metadata.get('network_function_id')):
         raise exceptions.IncompleteData("Amphora information is missing")
     if not self.get_amphora(loadbalancer_id):
         # REVISIT(jiahao): use network_function_id as amphora id
         amp = o_data_models.Amphora(
             lb_network_ip=sc_metadata['floating_ip'],
             id=sc_metadata['network_function_id'],
             status=status)
         self.amphorae[loadbalancer_id] = [amp]
    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_loadbalancer_octavia_model(self, loadbalancer_dict):
        loadbalancer = n_data_models.LoadBalancer.from_dict(
            copy.deepcopy(loadbalancer_dict))
        ret = o_data_models.LoadBalancer()
        args = self._get_common_args(loadbalancer)
        vip = o_data_models.Vip(
            load_balancer_id=loadbalancer.id,
            ip_address=loadbalancer.vip_address,
            subnet_id=loadbalancer.vip_subnet_id,
            port_id=loadbalancer.vip_port.id,
            load_balancer=ret
        )
        amphorae = self.driver.get_amphora(loadbalancer.id)
        if not amphorae:
            raise exceptions.IncompleteData(
                "Amphora information is missing")
        # REVISIT(jiahao): cluster_group, topology, affinity_group_id are not
        # included yet
        args.update({
            'vip': vip,
            'amphorae': amphorae,
            'provisioning_status': loadbalancer.provisioning_status,
        })
        if loadbalancer_dict.get('listeners'):
            listeners = []
            pools = []
            for listener_dict in loadbalancer_dict.get('listeners'):
                listener = self.get_listener_octavia_model(listener_dict)
                listener.load_balancer = ret
                listeners.append(listener)
                pools.extend(listener.pools)
                for pool in listener.pools:
                    if pool.id not in [pool.id for pool in pools]:
                        pools.append(pool)
            args.update({
                'listeners': listeners,
                'pools': pools,
            })

        ret = self._update(ret, args)
        return ret
    def _root_loadbalancer_id(self, obj_type, obj_dict):
        """Returns the loadbalancer id this instance is attached to."""

        try:
            # For Mitaka
            if obj_type == lb_const.LOADBALANCER:
                lb = obj_dict['id']
            elif obj_type == lb_const.LISTENER:
                lb = obj_dict[lb_const.LOADBALANCER]['id']
            elif obj_type == lb_const.L7POLICY:
                lb = obj_dict[lb_const.LISTENER][lb_const.LOADBALANCER]['id']
            elif obj_type == lb_const.L7RULE:
                lb = obj_dict['policy'][lb_const.LISTENER][
                    lb_const.LOADBALANCER]['id']
            elif obj_type == lb_const.POOL:
                lb = obj_dict[lb_const.LOADBALANCER]['id']
            elif obj_type == lb_const.SNI:
                lb = obj_dict[lb_const.LISTENER][lb_const.LOADBALANCER]['id']
            else:
                # Pool Member or Health Monitor
                lb = obj_dict[lb_const.POOL][lb_const.LOADBALANCER]['id']
            # For Liberty
            # if obj_type == lb_const.LOADBALANCER:
            #     lb = obj_dict['id']
            # elif obj_type == lb_const.LISTENER:
            #     lb = obj_dict[lb_const.LOADBALANCER]['id']
            # elif obj_type == lb_const.POOL:
            #     lb = obj_dict[lb_const.LISTENER][lb_const.LOADBALANCER]['id']
            # elif obj_type == lb_const.SNI:
            #     lb = obj_dict[lb_const.LISTENER][lb_const.LOADBALANCER]['id']
            # else:
            #     # Pool Member or Health Monitor
            #     lb = obj_dict[lb_const.POOL][lb_const.LISTENER][
            #         lb_const.LOADBALANCER]['id']
        except Exception:
            raise exceptions.IncompleteData(
                'Root loadbalancer id was not found')
        else:
            return lb
    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 get_network_function_device_config_info(self,
                                                device_data,
                                                network_handler=None):
        """ Get the configuration information for NFD

        :param device_data: NFD
        :type device_data: dict

        :returns: None -- On Failure
        :returns: dict -- It has the following scheme
        {
            'config': [
                {
                    'resource': 'interfaces',
                    'resource_data': {
                        ...
                    }
                },
                {
                    'resource': 'routes',
                    'resource_data': {
                        ...
                    }
                }
            ]
        }

        :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'])):
            raise exceptions.IncompleteData()

        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) = (network_handler.get_port_details(
                         token, port['id']))
                except Exception:
                    LOG.error(
                        _LE('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) = (network_handler.get_port_details(
                         token, port['id']))
                except Exception:
                    LOG.error(
                        _LE('Failed to get consumer port details'
                            ' for get device config info operation'))
                    return None

        return {
            'config': [{
                'resource': nfp_constants.INTERFACE_RESOURCE,
                'resource_data': {
                    'mgmt_ip': device_data['mgmt_ip_address'],
                    'provider_ip': provider_ip,
                    'provider_cidr': provider_cidr,
                    'provider_interface_index': 2,
                    'stitching_ip': consumer_ip,
                    'stitching_cidr': consumer_cidr,
                    'stitching_interface_index': 3,
                    'provider_mac': provider_mac,
                    'stitching_mac': consumer_mac,
                }
            }, {
                'resource': nfp_constants.ROUTES_RESOURCE,
                'resource_data': {
                    'mgmt_ip':
                    device_data['mgmt_ip_address'],
                    'source_cidrs': ([provider_cidr, consumer_cidr]
                                     if consumer_cidr else [provider_cidr]),
                    'destination_cidr':
                    consumer_cidr,
                    'provider_mac':
                    provider_mac,
                    'gateway_ip':
                    consumer_gateway_ip,
                    'provider_interface_index':
                    2
                }
            }]
        }
    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