<gh_stars>0
#
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import six

from nfv_common import debug
from nfv_common.helpers import coroutine
from nfv_common.helpers import Singleton

from nfv_vim import nfvi
from nfv_vim import objects
from nfv_vim import tables

from nfv_vim.directors._directors_defs import Operation
from nfv_vim.directors._directors_defs import OPERATION_STATE
from nfv_vim.directors._directors_defs import OPERATION_TYPE

DLOG = debug.debug_get_logger('nfv_vim.host_director')

_host_director = None


@six.add_metaclass(Singleton)
class HostDirector(object):
    """
    Host Director
    """
    def __init__(self):
        self._host_operation = None

    @coroutine
    def _nfvi_lock_host_callback(self):
        """
        NFVI Lock Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Lock Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Lock of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.LOCK_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_lock_failed(host)

    def _nfvi_lock_host(self, host_uuid, host_name):
        """
        NFVI Lock Host
        """
        nfvi.nfvi_lock_host(host_uuid, host_name, self._nfvi_lock_host_callback())

    @coroutine
    def _nfvi_disable_host_services_callback(self, service):
        """
        NFVI Disable Host Services Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Disable Host %s Services callback "
                     "response=%s." % (service, response))
        if not response['completed']:
            DLOG.info("Disable of %s services on host %s failed"
                      ", reason=%s."
                      % (service, response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.DISABLE_HOST_SERVICES != \
                    self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.disable_host_services_failed(host)

    def _nfvi_disable_host_services(self, host_uuid, host_name,
                                    host_personality, host_offline, service):
        """
        NFVI Disable Host Services
        """
        if service == objects.HOST_SERVICES.COMPUTE:
            nfvi.nfvi_disable_compute_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.COMPUTE))
        elif service == objects.HOST_SERVICES.GUEST:
            nfvi.nfvi_disable_guest_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.GUEST))
        elif service == objects.HOST_SERVICES.CONTAINER:
            nfvi.nfvi_disable_container_host_services(
                host_uuid, host_name, host_personality, host_offline,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.CONTAINER))
        else:
            DLOG.error("Trying to disable unknown service: %s" % service)

    @coroutine
    def _nfvi_enable_host_services_callback(self, service):
        """
        NFVI Enable Host Services Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Enable Host %s Services callback "
                     "response=%s." % (service, response))
        if not response['completed']:
            DLOG.info("Enable of %s services on host %s failed, reason=%s."
                      % (service, response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.ENABLE_HOST_SERVICES != \
                    self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name,
                                self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.enable_host_services_failed(host)

    def _nfvi_enable_host_services(self, host_uuid, host_name,
                                   host_personality, service):
        """
        NFVI Enable Host Services
        """
        if service == objects.HOST_SERVICES.COMPUTE:
            nfvi.nfvi_enable_compute_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.COMPUTE))
        elif service == objects.HOST_SERVICES.GUEST:
            nfvi.nfvi_enable_guest_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.GUEST))
        elif service == objects.HOST_SERVICES.CONTAINER:
            nfvi.nfvi_enable_container_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.CONTAINER))
        elif service == objects.HOST_SERVICES.NETWORK:
            nfvi.nfvi_enable_network_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.NETWORK))
        else:
            DLOG.error("Trying to enable unknown service: %s" % service)

    @coroutine
    def _nfvi_unlock_host_callback(self):
        """
        NFVI Unlock Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Unlock Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Unlock of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.UNLOCK_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_unlock_failed(host)

    def _nfvi_unlock_host(self, host_uuid, host_name):
        """
        NFVI Unlock Host
        """
        nfvi.nfvi_unlock_host(host_uuid, host_name,
                              self._nfvi_unlock_host_callback())

    @coroutine
    def _nfvi_reboot_host_callback(self):
        """
        NFVI Reboot Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Reboot Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Reboot of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.REBOOT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_reboot_failed(host)

    def _nfvi_reboot_host(self, host_uuid, host_name):
        """
        NFVI Reboot Host
        """
        nfvi.nfvi_reboot_host(host_uuid, host_name,
                              self._nfvi_reboot_host_callback())

    @coroutine
    def _nfvi_upgrade_host_callback(self):
        """
        NFVI Upgrade Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Upgrade Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Upgrade of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.UPGRADE_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_upgrade_failed(host)

    def _nfvi_upgrade_host(self, host_uuid, host_name):
        """
        NFVI Upgrade Host
        """
        nfvi.nfvi_upgrade_host(host_uuid, host_name,
                               self._nfvi_upgrade_host_callback())

    @coroutine
    def _nfvi_swact_host_callback(self):
        """
        NFVI Swact Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Swact Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Swact of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.SWACT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_swact_failed(host)

    def _nfvi_swact_host(self, host_uuid, host_name):
        """
        NFVI Swact Host
        """
        nfvi.nfvi_swact_from_host(host_uuid, host_name,
                                  self._nfvi_swact_host_callback())

    @coroutine
    def _nfvi_fw_update_host_callback(self):
        """
        NFVI Firmware Update Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Firmware Update Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Firmware Image Update for host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.FW_UPDATE_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_fw_update_failed(host)

    def _nfvi_fw_update_host(self, host_uuid, host_name):
        """
        NFVI Firmware Image Update Host
        """
        nfvi.nfvi_host_device_image_update(host_uuid, host_name, self._nfvi_fw_update_host_callback())

    @coroutine
    def _nfvi_fw_update_abort_callback(self):
        """
        NFVI Abort Firmware Update callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Abort Firmware Update callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Get Host Devices for host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.FW_UPDATE_ABORT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_fw_update_abort_failed(host)

    def _nfvi_fw_update_abort_host(self, host_uuid, host_name):
        """
        NFVI Abort Firmware Update
        """
        nfvi.nfvi_host_device_image_update_abort(host_uuid, host_name, self._nfvi_fw_update_abort_callback())

    def host_operation_inprogress(self):
        """
        Returns true if a lock of hosts
        """
        if self._host_operation is not None:
            return self._host_operation.is_inprogress()
        return False

    @staticmethod
    def host_has_instances(host, skip_stopped=False):
        """
        Returns true if a host has instances located on it
        """
        from nfv_vim import directors

        instance_director = directors.get_instance_director()
        return instance_director.host_has_instances(host, skip_stopped=skip_stopped)

    @staticmethod
    def host_instances_moved(host, host_operation):
        """
        Notifies the host director that all the instances have been moved from
        a host
        """
        host.notify_instances_moved(host_operation)

    @staticmethod
    def host_instances_stopped(host, host_operation):
        """
        Notifies the host director that all the instances have been stopped on
        a host
        """
        host.notify_instances_stopped(host_operation)

    @staticmethod
    def host_enabled(host):
        """
        Notifies the host director that a host is enabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is enabled."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.recover_instances()

    @staticmethod
    def host_services_disabling(host):
        """
        Notifies the host director that host services are being disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s services are "
                  "disabling." % host.name)
        instance_director = directors.get_instance_director()
        host_operation = instance_director.host_services_disabling(host)
        return host_operation

    @staticmethod
    def host_services_disabled(host):
        """
        Notifies the host director that host services are disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s services are "
                  "disabled." % host.name)
        instance_director = directors.get_instance_director()
        host_operation = instance_director.host_services_disabled(host)
        return host_operation

    @staticmethod
    def host_disabled(host):
        """
        Notifies the host director that a host is disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is disabled."
                  % host.name)

        instance_director = directors.get_instance_director()
        instance_director.host_disabled(host)

    @staticmethod
    def host_offline(host):
        """
        Notifies the host director that a host is offline
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is offline."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_offline(host)
        # Now that the host is offline, we may be able to recover instances
        # on that host (i.e. evacuate them).
        instance_director.recover_instances()

    @staticmethod
    def host_audit(host):
        """
        Notifies the host director that a host audit is inprogress
        """
        from nfv_vim import directors

        DLOG.verbose("Notify other directors that a host %s audit is inprogress."
                     % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_audit(host)

        sw_mgmt_director = directors.get_sw_mgmt_director()
        sw_mgmt_director.host_audit(host)

    @staticmethod
    def host_abort(host):
        """
        Notifies the host director that a host abort is inprogress
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that a host %s abort is inprogress."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_operation_cancel(host.name)

    @staticmethod
    def host_state_change_notify(host):
        """
        Notifies the host director that a host has changed state
        """
        from nfv_vim import directors

        DLOG.info("Host %s state change notification." % host.name)

        sw_mgmt_director = directors.get_sw_mgmt_director()
        sw_mgmt_director.host_state_change(host)

    def lock_hosts(self, host_names):
        """
        Lock a list of hosts
        """
        DLOG.info("Lock hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.LOCK_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locking():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

            elif host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)

            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_lock_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def unlock_hosts(self, host_names):
        """
        Unlock a list of hosts
        """
        DLOG.info("Unlock hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.UNLOCK_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_unlock_host(host.uuid, host.name)

            elif host.is_unlocking():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def reboot_hosts(self, host_names):
        """
        Reboot a list of hosts
        """
        DLOG.info("Reboot hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.REBOOT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_reboot_host(host.uuid, host.name)

            else:
                reason = "Cannot reboot unlocked host %s." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def upgrade_hosts(self, host_names):
        """
        Upgrade a list of hosts
        """
        DLOG.info("Upgrade hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.UPGRADE_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_upgrade_host(host.uuid, host.name)

            else:
                reason = "Cannot upgrade unlocked host %s." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def swact_hosts(self, host_names):
        """
        Swact a list of hosts
        """
        DLOG.info("Swact hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.SWACT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_swact_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def fw_update_hosts(self, host_names):
        """
        Firmware Update hosts
        """
        DLOG.info("Firmware Update hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.FW_UPDATE_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_fw_update_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def fw_update_abort_hosts(self, host_names):
        """
        Firmware Update Abort Hosts
        """
        DLOG.info("Firmware Update Abort for hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.FW_UPDATE_ABORT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_fw_update_abort_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def disable_host_services(self, host_names, service):
        """
        Disable a host service on a list of hosts
        """
        DLOG.info("Disable host services: %s service: %s" %
                  (host_names, service))

        host_operation = Operation(OPERATION_TYPE.DISABLE_HOST_SERVICES)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        host_list = list()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host.host_services_locked = True
            if (objects.HOST_SERVICE_STATE.DISABLED ==
                    host.host_service_state(service)):
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)
            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                host_list.append(host)

        for host in host_list:
            self._nfvi_disable_host_services(
                host.uuid, host.name, host.personality, host.is_offline,
                service)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def enable_host_services(self, host_names, service):
        """
        Enable a host service on a list of hosts
        """
        DLOG.info("Enable host services: %s service: %s" %
                  (host_names, service))

        host_operation = Operation(OPERATION_TYPE.ENABLE_HOST_SERVICES)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        host_list = list()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host.host_services_locked = False
            if (objects.HOST_SERVICE_STATE.ENABLED ==
                    host.host_service_state(service)):
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)
            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                host_list.append(host)

        for host in host_list:
            self._nfvi_enable_host_services(
                host.uuid, host.name, host.personality, service)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation


def get_host_director():
    """
    Returns the Host Director
    """
    return _host_director


def host_director_initialize():
    """
    Initialize Host Director
    """
    global _host_director

    _host_director = HostDirector()


def host_director_finalize():
    """
    Finalize Host Director
    """
    pass

#
# Copyright (c) 2015-2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import six

from nfv_common import debug
from nfv_common.helpers import coroutine
from nfv_common.helpers import Singleton

from nfv_vim import nfvi
from nfv_vim import objects
from nfv_vim import tables

from nfv_vim.directors._directors_defs import Operation
from nfv_vim.directors._directors_defs import OPERATION_STATE
from nfv_vim.directors._directors_defs import OPERATION_TYPE

DLOG = debug.debug_get_logger('nfv_vim.host_director')

_host_director = None


@six.add_metaclass(Singleton)
class HostDirector(object):
    """
    Host Director
    """
    def __init__(self):
        self._host_operation = None

    @coroutine
    def _nfvi_lock_host_callback(self):
        """
        NFVI Lock Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Lock Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Lock of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.LOCK_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_lock_failed(host)

    def _nfvi_lock_host(self, host_uuid, host_name):
        """
        NFVI Lock Host
        """
        nfvi.nfvi_lock_host(host_uuid, host_name, self._nfvi_lock_host_callback())

    @coroutine
    def _nfvi_disable_host_services_callback(self, service):
        """
        NFVI Disable Host Services Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Disable Host %s Services callback "
                     "response=%s." % (service, response))
        if not response['completed']:
            DLOG.info("Disable of %s services on host %s failed"
                      ", reason=%s."
                      % (service, response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.DISABLE_HOST_SERVICES != \
                    self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.disable_host_services_failed(host)

    def _nfvi_disable_host_services(self, host_uuid, host_name,
                                    host_personality, host_offline, service):
        """
        NFVI Disable Host Services
        """
        if service == objects.HOST_SERVICES.COMPUTE:
            nfvi.nfvi_disable_compute_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.COMPUTE))
        elif service == objects.HOST_SERVICES.GUEST:
            nfvi.nfvi_disable_guest_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.GUEST))
        elif service == objects.HOST_SERVICES.CONTAINER:
            nfvi.nfvi_disable_container_host_services(
                host_uuid, host_name, host_personality, host_offline,
                self._nfvi_disable_host_services_callback(
                    objects.HOST_SERVICES.CONTAINER))
        else:
            DLOG.error("Trying to disable unknown service: %s" % service)

    @coroutine
    def _nfvi_enable_host_services_callback(self, service):
        """
        NFVI Enable Host Services Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Enable Host %s Services callback "
                     "response=%s." % (service, response))
        if not response['completed']:
            DLOG.info("Enable of %s services on host %s failed, reason=%s."
                      % (service, response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.ENABLE_HOST_SERVICES != \
                    self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name,
                                self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.enable_host_services_failed(host)

    def _nfvi_enable_host_services(self, host_uuid, host_name,
                                   host_personality, service):
        """
        NFVI Enable Host Services
        """
        if service == objects.HOST_SERVICES.COMPUTE:
            nfvi.nfvi_enable_compute_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.COMPUTE))
        elif service == objects.HOST_SERVICES.GUEST:
            nfvi.nfvi_enable_guest_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.GUEST))
        elif service == objects.HOST_SERVICES.CONTAINER:
            nfvi.nfvi_enable_container_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.CONTAINER))
        elif service == objects.HOST_SERVICES.NETWORK:
            nfvi.nfvi_enable_network_host_services(
                host_uuid, host_name, host_personality,
                self._nfvi_enable_host_services_callback(
                    objects.HOST_SERVICES.NETWORK))
        else:
            DLOG.error("Trying to enable unknown service: %s" % service)

    @coroutine
    def _nfvi_unlock_host_callback(self):
        """
        NFVI Unlock Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Unlock Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Unlock of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.UNLOCK_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_unlock_failed(host)

    def _nfvi_unlock_host(self, host_uuid, host_name):
        """
        NFVI Unlock Host
        """
        nfvi.nfvi_unlock_host(host_uuid, host_name,
                              self._nfvi_unlock_host_callback())

    @coroutine
    def _nfvi_reboot_host_callback(self):
        """
        NFVI Reboot Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Reboot Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Reboot of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.REBOOT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_reboot_failed(host)

    def _nfvi_reboot_host(self, host_uuid, host_name):
        """
        NFVI Reboot Host
        """
        nfvi.nfvi_reboot_host(host_uuid, host_name,
                              self._nfvi_reboot_host_callback())

    @coroutine
    def _nfvi_upgrade_host_callback(self):
        """
        NFVI Upgrade Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Upgrade Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Upgrade of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.UPGRADE_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_upgrade_failed(host)

    def _nfvi_upgrade_host(self, host_uuid, host_name):
        """
        NFVI Upgrade Host
        """
        nfvi.nfvi_upgrade_host(host_uuid, host_name,
                               self._nfvi_upgrade_host_callback())

    @coroutine
    def _nfvi_swact_host_callback(self):
        """
        NFVI Swact Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Swact Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Swact of host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.SWACT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_swact_failed(host)

    def _nfvi_swact_host(self, host_uuid, host_name):
        """
        NFVI Swact Host
        """
        nfvi.nfvi_swact_from_host(host_uuid, host_name,
                                  self._nfvi_swact_host_callback())

    @coroutine
    def _nfvi_fw_update_host_callback(self):
        """
        NFVI Firmware Update Host Callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Firmware Update Host callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Firmware Image Update for host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.FW_UPDATE_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_fw_update_failed(host)

    def _nfvi_fw_update_host(self, host_uuid, host_name):
        """
        NFVI Firmware Image Update Host
        """
        nfvi.nfvi_host_device_image_update(host_uuid, host_name, self._nfvi_fw_update_host_callback())

    @coroutine
    def _nfvi_fw_update_abort_callback(self):
        """
        NFVI Abort Firmware Update callback
        """
        from nfv_vim import directors

        response = (yield)
        DLOG.verbose("NFVI Abort Firmware Update callback response=%s." % response)
        if not response['completed']:
            DLOG.info("Get Host Devices for host %s failed, reason=%s."
                      % (response['host_name'], response['reason']))

            host_table = tables.tables_get_host_table()
            host = host_table.get(response['host_name'], None)
            if host is None:
                DLOG.verbose("Host %s does not exist." % response['host_name'])
                return

            if self._host_operation is None:
                DLOG.verbose("No host %s operation inprogress." % host.name)
                return

            if OPERATION_TYPE.FW_UPDATE_ABORT_HOSTS != self._host_operation.operation_type:
                DLOG.verbose("Unexpected host %s operation %s, ignoring."
                             % (host.name, self._host_operation.operation_type))
                return

            sw_mgmt_director = directors.get_sw_mgmt_director()
            sw_mgmt_director.host_fw_update_abort_failed(host)

    def _nfvi_fw_update_abort_host(self, host_uuid, host_name):
        """
        NFVI Abort Firmware Update
        """
        nfvi.nfvi_host_device_image_update_abort(host_uuid, host_name, self._nfvi_fw_update_abort_callback())

    def host_operation_inprogress(self):
        """
        Returns true if a lock of hosts
        """
        if self._host_operation is not None:
            return self._host_operation.is_inprogress()
        return False

    @staticmethod
    def host_has_instances(host, skip_stopped=False):
        """
        Returns true if a host has instances located on it
        """
        from nfv_vim import directors

        instance_director = directors.get_instance_director()
        return instance_director.host_has_instances(host, skip_stopped=skip_stopped)

    @staticmethod
    def host_instances_moved(host, host_operation):
        """
        Notifies the host director that all the instances have been moved from
        a host
        """
        host.notify_instances_moved(host_operation)

    @staticmethod
    def host_instances_stopped(host, host_operation):
        """
        Notifies the host director that all the instances have been stopped on
        a host
        """
        host.notify_instances_stopped(host_operation)

    @staticmethod
    def host_enabled(host):
        """
        Notifies the host director that a host is enabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is enabled."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.recover_instances()

    @staticmethod
    def host_services_disabling(host):
        """
        Notifies the host director that host services are being disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s services are "
                  "disabling." % host.name)
        instance_director = directors.get_instance_director()
        host_operation = instance_director.host_services_disabling(host)
        return host_operation

    @staticmethod
    def host_services_disabled(host):
        """
        Notifies the host director that host services are disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s services are "
                  "disabled." % host.name)
        instance_director = directors.get_instance_director()
        host_operation = instance_director.host_services_disabled(host)
        return host_operation

    @staticmethod
    def host_disabled(host):
        """
        Notifies the host director that a host is disabled
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is disabled."
                  % host.name)

        instance_director = directors.get_instance_director()
        instance_director.host_disabled(host)

    @staticmethod
    def host_offline(host):
        """
        Notifies the host director that a host is offline
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that the host %s is offline."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_offline(host)
        # Now that the host is offline, we may be able to recover instances
        # on that host (i.e. evacuate them).
        instance_director.recover_instances()

    @staticmethod
    def host_audit(host):
        """
        Notifies the host director that a host audit is inprogress
        """
        from nfv_vim import directors

        DLOG.verbose("Notify other directors that a host %s audit is inprogress."
                     % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_audit(host)

        sw_mgmt_director = directors.get_sw_mgmt_director()
        sw_mgmt_director.host_audit(host)

    @staticmethod
    def host_abort(host):
        """
        Notifies the host director that a host abort is inprogress
        """
        from nfv_vim import directors

        DLOG.info("Notify other directors that a host %s abort is inprogress."
                  % host.name)
        instance_director = directors.get_instance_director()
        instance_director.host_operation_cancel(host.name)

    @staticmethod
    def host_state_change_notify(host):
        """
        Notifies the host director that a host has changed state
        """
        from nfv_vim import directors

        DLOG.info("Host %s state change notification." % host.name)

        sw_mgmt_director = directors.get_sw_mgmt_director()
        sw_mgmt_director.host_state_change(host)

    def lock_hosts(self, host_names):
        """
        Lock a list of hosts
        """
        DLOG.info("Lock hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.LOCK_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locking():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

            elif host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)

            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_lock_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def unlock_hosts(self, host_names):
        """
        Unlock a list of hosts
        """
        DLOG.info("Unlock hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.UNLOCK_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_unlock_host(host.uuid, host.name)

            elif host.is_unlocking():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def reboot_hosts(self, host_names):
        """
        Reboot a list of hosts
        """
        DLOG.info("Reboot hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.REBOOT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_reboot_host(host.uuid, host.name)

            else:
                reason = "Cannot reboot unlocked host %s." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def upgrade_hosts(self, host_names):
        """
        Upgrade a list of hosts
        """
        DLOG.info("Upgrade hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.UPGRADE_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            if host.is_locked():
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                self._nfvi_upgrade_host(host.uuid, host.name)

            else:
                reason = "Cannot upgrade unlocked host %s." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def swact_hosts(self, host_names):
        """
        Swact a list of hosts
        """
        DLOG.info("Swact hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.SWACT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_swact_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def fw_update_hosts(self, host_names):
        """
        Firmware Update hosts
        """
        DLOG.info("Firmware Update hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.FW_UPDATE_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_fw_update_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def fw_update_abort_hosts(self, host_names):
        """
        Firmware Update Abort Hosts
        """
        DLOG.info("Firmware Update Abort for hosts: %s" % host_names)

        host_operation = Operation(OPERATION_TYPE.FW_UPDATE_ABORT_HOSTS)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
            self._nfvi_fw_update_abort_host(host.uuid, host.name)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def disable_host_services(self, host_names, service):
        """
        Disable a host service on a list of hosts
        """
        DLOG.info("Disable host services: %s service: %s" %
                  (host_names, service))

        host_operation = Operation(OPERATION_TYPE.DISABLE_HOST_SERVICES)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        host_list = list()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host.host_services_locked = True
            if (objects.HOST_SERVICE_STATE.DISABLED ==
                    host.host_service_state(service)):
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)
            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                host_list.append(host)

        for host in host_list:
            self._nfvi_disable_host_services(
                host.uuid, host.name, host.personality, host.is_offline,
                service)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation

    def enable_host_services(self, host_names, service):
        """
        Enable a host service on a list of hosts
        """
        DLOG.info("Enable host services: %s service: %s" %
                  (host_names, service))

        host_operation = Operation(OPERATION_TYPE.ENABLE_HOST_SERVICES)

        if self._host_operation is not None:
            DLOG.debug("Canceling previous host operation %s, before "
                       "continuing with host operation %s."
                       % (self._host_operation.operation_type,
                          host_operation.operation_type))
            self._host_operation = None

        host_table = tables.tables_get_host_table()
        host_list = list()
        for host_name in host_names:
            host = host_table.get(host_name, None)
            if host is None:
                reason = "Unknown host %s given." % host_name
                DLOG.info(reason)
                host_operation.set_failed(reason)
                return host_operation

            host.host_services_locked = False
            if (objects.HOST_SERVICE_STATE.ENABLED ==
                    host.host_service_state(service)):
                host_operation.add_host(host.name, OPERATION_STATE.COMPLETED)
            else:
                host_operation.add_host(host.name, OPERATION_STATE.INPROGRESS)
                host_list.append(host)

        for host in host_list:
            self._nfvi_enable_host_services(
                host.uuid, host.name, host.personality, service)

        if host_operation.is_inprogress():
            self._host_operation = host_operation

        return host_operation


def get_host_director():
    """
    Returns the Host Director
    """
    return _host_director


def host_director_initialize():
    """
    Initialize Host Director
    """
    global _host_director

    _host_director = HostDirector()


def host_director_finalize():
    """
    Finalize Host Director
    """
    pass
# # Copyright (c) 2015-2020 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # Host Director NFVI Lock Host Callback NFVI Lock Host NFVI Disable Host Services Callback NFVI Disable Host Services NFVI Enable Host Services Callback NFVI Enable Host Services NFVI Unlock Host Callback NFVI Unlock Host NFVI Reboot Host Callback NFVI Reboot Host NFVI Upgrade Host Callback NFVI Upgrade Host NFVI Swact Host Callback NFVI Swact Host NFVI Firmware Update Host Callback NFVI Firmware Image Update Host NFVI Abort Firmware Update callback NFVI Abort Firmware Update Returns true if a lock of hosts Returns true if a host has instances located on it Notifies the host director that all the instances have been moved from         a host Notifies the host director that all the instances have been stopped on         a host Notifies the host director that a host is enabled Notifies the host director that host services are being disabled Notifies the host director that host services are disabled Notifies the host director that a host is disabled Notifies the host director that a host is offline # Now that the host is offline, we may be able to recover instances # on that host (i.e. evacuate them). Notifies the host director that a host audit is inprogress Notifies the host director that a host abort is inprogress Notifies the host director that a host has changed state Lock a list of hosts Unlock a list of hosts Reboot a list of hosts Upgrade a list of hosts Swact a list of hosts Firmware Update hosts Firmware Update Abort Hosts Disable a host service on a list of hosts Enable a host service on a list of hosts Returns the Host Director Initialize Host Director Finalize Host Director