"Fossies" - the Fresh Open Source Software Archive

Member "nova-22.0.1/nova/virt/libvirt/host.py" (19 Nov 2020, 52211 Bytes) of package /linux/misc/openstack/nova-22.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "host.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 22.0.0_vs_22.0.1.

    1 # Copyright 2010 United States Government as represented by the
    2 # Administrator of the National Aeronautics and Space Administration.
    3 # All Rights Reserved.
    4 # Copyright (c) 2010 Citrix Systems, Inc.
    5 # Copyright (c) 2011 Piston Cloud Computing, Inc
    6 # Copyright (c) 2012 University Of Minho
    7 # (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
    8 #
    9 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
   10 #    not use this file except in compliance with the License. You may obtain
   11 #    a copy of the License at
   12 #
   13 #         http://www.apache.org/licenses/LICENSE-2.0
   14 #
   15 #    Unless required by applicable law or agreed to in writing, software
   16 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   17 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   18 #    License for the specific language governing permissions and limitations
   19 #    under the License.
   20 
   21 """
   22 Manages information about the host OS and hypervisor.
   23 
   24 This class encapsulates a connection to the libvirt
   25 daemon and provides certain higher level APIs around
   26 the raw libvirt API. These APIs are then used by all
   27 the other libvirt related classes
   28 """
   29 
   30 from collections import defaultdict
   31 import inspect
   32 import operator
   33 import os
   34 import queue
   35 import socket
   36 import threading
   37 import typing as ty
   38 
   39 from eventlet import greenio
   40 from eventlet import greenthread
   41 from eventlet import patcher
   42 from eventlet import tpool
   43 from oslo_log import log as logging
   44 from oslo_utils import excutils
   45 from oslo_utils import importutils
   46 from oslo_utils import units
   47 from oslo_utils import versionutils
   48 
   49 from nova.compute import utils as compute_utils
   50 import nova.conf
   51 from nova import context as nova_context
   52 from nova import exception
   53 from nova.i18n import _
   54 from nova import rpc
   55 from nova import utils
   56 from nova.virt import event as virtevent
   57 from nova.virt.libvirt import config as vconfig
   58 from nova.virt.libvirt import guest as libvirt_guest
   59 from nova.virt.libvirt import migration as libvirt_migrate
   60 from nova.virt.libvirt import utils as libvirt_utils
   61 
   62 if ty.TYPE_CHECKING:
   63     import libvirt
   64 else:
   65     libvirt = None
   66 
   67 LOG = logging.getLogger(__name__)
   68 
   69 native_socket = patcher.original('socket')
   70 native_threading = patcher.original("threading")
   71 native_Queue = patcher.original("queue")
   72 
   73 CONF = nova.conf.CONF
   74 
   75 
   76 # This list is for libvirt hypervisor drivers that need special handling.
   77 # This is *not* the complete list of supported hypervisor drivers.
   78 HV_DRIVER_QEMU = "QEMU"
   79 HV_DRIVER_XEN = "Xen"
   80 
   81 SEV_KERNEL_PARAM_FILE = '/sys/module/kvm_amd/parameters/sev'
   82 
   83 
   84 class Host(object):
   85 
   86     def __init__(self, uri, read_only=False,
   87                  conn_event_handler=None,
   88                  lifecycle_event_handler=None):
   89 
   90         global libvirt
   91         if libvirt is None:
   92             libvirt = importutils.import_module('libvirt')
   93 
   94         self._uri = uri
   95         self._read_only = read_only
   96         self._initial_connection = True
   97         self._conn_event_handler = conn_event_handler
   98         self._conn_event_handler_queue: queue.Queue[ty.Callable] = (
   99           queue.Queue())
  100         self._lifecycle_event_handler = lifecycle_event_handler
  101         self._caps = None
  102         self._domain_caps = None
  103         self._hostname = None
  104 
  105         self._wrapped_conn = None
  106         self._wrapped_conn_lock = threading.Lock()
  107         self._event_queue: ty.Optional[queue.Queue[ty.Callable]] = None
  108 
  109         self._events_delayed = {}
  110         # Note(toabctl): During a reboot of a domain, STOPPED and
  111         #                STARTED events are sent. To prevent shutting
  112         #                down the domain during a reboot, delay the
  113         #                STOPPED lifecycle event some seconds.
  114         self._lifecycle_delay = 15
  115 
  116         self._initialized = False
  117         self._libvirt_proxy_classes = self._get_libvirt_proxy_classes(libvirt)
  118         self._libvirt_proxy = self._wrap_libvirt_proxy(libvirt)
  119 
  120         # AMD SEV is conditional on support in the hardware, kernel,
  121         # qemu, and libvirt.  This is determined on demand and
  122         # memoized by the supports_amd_sev property below.
  123         self._supports_amd_sev = None
  124 
  125         self._has_hyperthreading = None
  126 
  127     @staticmethod
  128     def _get_libvirt_proxy_classes(libvirt_module):
  129         """Return a tuple for tpool.Proxy's autowrap argument containing all
  130         public vir* classes defined by the libvirt module.
  131         """
  132 
  133         # Get a list of (name, class) tuples of libvirt classes
  134         classes = inspect.getmembers(libvirt_module, inspect.isclass)
  135 
  136         # Return a list of just the vir* classes, filtering out libvirtError
  137         # and any private globals pointing at private internal classes.
  138         return tuple([cls[1] for cls in classes if cls[0].startswith("vir")])
  139 
  140     def _wrap_libvirt_proxy(self, obj):
  141         """Return an object wrapped in a tpool.Proxy using autowrap appropriate
  142         for the libvirt module.
  143         """
  144 
  145         # libvirt is not pure python, so eventlet monkey patching doesn't work
  146         # on it. Consequently long-running libvirt calls will not yield to
  147         # eventlet's event loop, starving all other greenthreads until
  148         # completion. eventlet's tpool.Proxy handles this situation for us by
  149         # executing proxied calls in a native thread.
  150         return tpool.Proxy(obj, autowrap=self._libvirt_proxy_classes)
  151 
  152     def _native_thread(self):
  153         """Receives async events coming in from libvirtd.
  154 
  155         This is a native thread which runs the default
  156         libvirt event loop implementation. This processes
  157         any incoming async events from libvirtd and queues
  158         them for later dispatch. This thread is only
  159         permitted to use libvirt python APIs, and the
  160         driver.queue_event method. In particular any use
  161         of logging is forbidden, since it will confuse
  162         eventlet's greenthread integration
  163         """
  164 
  165         while True:
  166             libvirt.virEventRunDefaultImpl()
  167 
  168     def _dispatch_thread(self):
  169         """Dispatches async events coming in from libvirtd.
  170 
  171         This is a green thread which waits for events to
  172         arrive from the libvirt event loop thread. This
  173         then dispatches the events to the compute manager.
  174         """
  175 
  176         while True:
  177             self._dispatch_events()
  178 
  179     def _conn_event_thread(self):
  180         """Dispatches async connection events"""
  181         # NOTE(mdbooth): This thread doesn't need to jump through the same
  182         # hoops as _dispatch_thread because it doesn't interact directly
  183         # with the libvirt native thread.
  184         while True:
  185             self._dispatch_conn_event()
  186 
  187     def _dispatch_conn_event(self):
  188         # NOTE(mdbooth): Splitting out this loop looks redundant, but it
  189         # means we can easily dispatch events synchronously from tests and
  190         # it isn't completely awful.
  191         handler = self._conn_event_handler_queue.get()
  192         try:
  193             handler()
  194         except Exception:
  195             LOG.exception('Exception handling connection event')
  196         finally:
  197             self._conn_event_handler_queue.task_done()
  198 
  199     @staticmethod
  200     def _event_lifecycle_callback(conn, dom, event, detail, opaque):
  201         """Receives lifecycle events from libvirt.
  202 
  203         NB: this method is executing in a native thread, not
  204         an eventlet coroutine. It can only invoke other libvirt
  205         APIs, or use self._queue_event(). Any use of logging APIs
  206         in particular is forbidden.
  207         """
  208 
  209         self = opaque
  210 
  211         uuid = dom.UUIDString()
  212         transition = None
  213         if event == libvirt.VIR_DOMAIN_EVENT_STOPPED:
  214             transition = virtevent.EVENT_LIFECYCLE_STOPPED
  215         elif event == libvirt.VIR_DOMAIN_EVENT_STARTED:
  216             transition = virtevent.EVENT_LIFECYCLE_STARTED
  217         elif event == libvirt.VIR_DOMAIN_EVENT_SUSPENDED:
  218             if detail == libvirt.VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY:
  219                 transition = virtevent.EVENT_LIFECYCLE_POSTCOPY_STARTED
  220             elif detail == libvirt.VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED:
  221                 # VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED is also sent when live
  222                 # migration of the guest fails, so we cannot simply rely
  223                 # on the event itself but need to check if the job itself was
  224                 # successful.
  225                 # NOTE(mriedem): The job check logic here is copied from
  226                 # LibvirtDriver._live_migration_monitor.
  227                 guest = libvirt_guest.Guest(dom)
  228                 info = guest.get_job_info()
  229                 if info.type == libvirt.VIR_DOMAIN_JOB_NONE:
  230                     # Either still running, or failed or completed,
  231                     # lets untangle the mess.
  232                     info.type = libvirt_migrate.find_job_type(
  233                         guest, instance=None, logging_ok=False)
  234 
  235                 if info.type == libvirt.VIR_DOMAIN_JOB_COMPLETED:
  236                     transition = virtevent.EVENT_LIFECYCLE_MIGRATION_COMPLETED
  237                 else:
  238                     # Failed or some other status we don't know about, so just
  239                     # opt to report the guest is paused.
  240                     transition = virtevent.EVENT_LIFECYCLE_PAUSED
  241             else:
  242                 transition = virtevent.EVENT_LIFECYCLE_PAUSED
  243         elif event == libvirt.VIR_DOMAIN_EVENT_RESUMED:
  244             transition = virtevent.EVENT_LIFECYCLE_RESUMED
  245 
  246         if transition is not None:
  247             self._queue_event(virtevent.LifecycleEvent(uuid, transition))
  248 
  249     def _close_callback(self, conn, reason, opaque):
  250         close_info = {'conn': conn, 'reason': reason}
  251         self._queue_event(close_info)
  252 
  253     @staticmethod
  254     def _test_connection(conn):
  255         try:
  256             conn.getLibVersion()
  257             return True
  258         except libvirt.libvirtError as e:
  259             if (e.get_error_code() in (libvirt.VIR_ERR_SYSTEM_ERROR,
  260                                        libvirt.VIR_ERR_INTERNAL_ERROR) and
  261                 e.get_error_domain() in (libvirt.VIR_FROM_REMOTE,
  262                                          libvirt.VIR_FROM_RPC)):
  263                 LOG.debug('Connection to libvirt broke')
  264                 return False
  265             raise
  266 
  267     @staticmethod
  268     def _connect_auth_cb(creds, opaque):
  269         if len(creds) == 0:
  270             return 0
  271         raise exception.InternalError(
  272             _("Can not handle authentication request for %d credentials")
  273             % len(creds))
  274 
  275     def _connect(self, uri, read_only):
  276         auth = [[libvirt.VIR_CRED_AUTHNAME,
  277                  libvirt.VIR_CRED_ECHOPROMPT,
  278                  libvirt.VIR_CRED_REALM,
  279                  libvirt.VIR_CRED_PASSPHRASE,
  280                  libvirt.VIR_CRED_NOECHOPROMPT,
  281                  libvirt.VIR_CRED_EXTERNAL],
  282                 Host._connect_auth_cb,
  283                 None]
  284 
  285         flags = 0
  286         if read_only:
  287             flags = libvirt.VIR_CONNECT_RO
  288         return self._libvirt_proxy.openAuth(uri, auth, flags)
  289 
  290     def _queue_event(self, event):
  291         """Puts an event on the queue for dispatch.
  292 
  293         This method is called by the native event thread to
  294         put events on the queue for later dispatch by the
  295         green thread. Any use of logging APIs is forbidden.
  296         """
  297 
  298         if self._event_queue is None:
  299             return
  300 
  301         # Queue the event...
  302         self._event_queue.put(event)
  303 
  304         # ...then wakeup the green thread to dispatch it
  305         c = ' '.encode()
  306         self._event_notify_send.write(c)
  307         self._event_notify_send.flush()
  308 
  309     def _dispatch_events(self):
  310         """Wait for & dispatch events from native thread
  311 
  312         Blocks until native thread indicates some events
  313         are ready. Then dispatches all queued events.
  314         """
  315 
  316         # Wait to be notified that there are some
  317         # events pending
  318         try:
  319             _c = self._event_notify_recv.read(1)
  320             assert _c
  321         except ValueError:
  322             return  # will be raised when pipe is closed
  323 
  324         # Process as many events as possible without
  325         # blocking
  326         last_close_event = None
  327         # required for mypy
  328         if self._event_queue is None:
  329             return
  330         while not self._event_queue.empty():
  331             try:
  332                 event_type = ty.Union[
  333                     virtevent.LifecycleEvent, ty.Mapping[str, ty.Any]]
  334                 event: event_type = self._event_queue.get(block=False)
  335                 if isinstance(event, virtevent.LifecycleEvent):
  336                     # call possibly with delay
  337                     self._event_emit_delayed(event)
  338 
  339                 elif 'conn' in event and 'reason' in event:
  340                     last_close_event = event
  341             except native_Queue.Empty:
  342                 pass
  343         if last_close_event is None:
  344             return
  345         conn = last_close_event['conn']
  346         # get_new_connection may already have disabled the host,
  347         # in which case _wrapped_conn is None.
  348         with self._wrapped_conn_lock:
  349             if conn == self._wrapped_conn:
  350                 reason = str(last_close_event['reason'])
  351                 msg = _("Connection to libvirt lost: %s") % reason
  352                 self._wrapped_conn = None
  353                 self._queue_conn_event_handler(False, msg)
  354 
  355     def _event_emit_delayed(self, event):
  356         """Emit events - possibly delayed."""
  357         def event_cleanup(gt, *args, **kwargs):
  358             """Callback function for greenthread. Called
  359             to cleanup the _events_delayed dictionary when an event
  360             was called.
  361             """
  362             event = args[0]
  363             self._events_delayed.pop(event.uuid, None)
  364 
  365         # Cleanup possible delayed stop events.
  366         if event.uuid in self._events_delayed.keys():
  367             self._events_delayed[event.uuid].cancel()
  368             self._events_delayed.pop(event.uuid, None)
  369             LOG.debug("Removed pending event for %s due to "
  370                       "lifecycle event", event.uuid)
  371 
  372         if event.transition == virtevent.EVENT_LIFECYCLE_STOPPED:
  373             # Delay STOPPED event, as they may be followed by a STARTED
  374             # event in case the instance is rebooting
  375             id_ = greenthread.spawn_after(self._lifecycle_delay,
  376                                           self._event_emit, event)
  377             self._events_delayed[event.uuid] = id_
  378             # add callback to cleanup self._events_delayed dict after
  379             # event was called
  380             id_.link(event_cleanup, event)
  381         else:
  382             self._event_emit(event)
  383 
  384     def _event_emit(self, event):
  385         if self._lifecycle_event_handler is not None:
  386             self._lifecycle_event_handler(event)
  387 
  388     def _init_events_pipe(self):
  389         """Create a self-pipe for the native thread to synchronize on.
  390 
  391         This code is taken from the eventlet tpool module, under terms
  392         of the Apache License v2.0.
  393         """
  394 
  395         self._event_queue = native_Queue.Queue()
  396         try:
  397             rpipe, wpipe = os.pipe()
  398             self._event_notify_send = greenio.GreenPipe(wpipe, 'wb', 0)
  399             self._event_notify_recv = greenio.GreenPipe(rpipe, 'rb', 0)
  400         except (ImportError, NotImplementedError):
  401             # This is Windows compatibility -- use a socket instead
  402             #  of a pipe because pipes don't really exist on Windows.
  403             sock = native_socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  404             sock.bind(('localhost', 0))
  405             sock.listen(50)
  406             csock = native_socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  407             csock.connect(('localhost', sock.getsockname()[1]))
  408             nsock, addr = sock.accept()
  409             self._event_notify_send = nsock.makefile('wb', 0)
  410             gsock = greenio.GreenSocket(csock)
  411             self._event_notify_recv = gsock.makefile('rb', 0)
  412 
  413     def _init_events(self):
  414         """Initializes the libvirt events subsystem.
  415 
  416         This requires running a native thread to provide the
  417         libvirt event loop integration. This forwards events
  418         to a green thread which does the actual dispatching.
  419         """
  420 
  421         self._init_events_pipe()
  422 
  423         LOG.debug("Starting native event thread")
  424         self._event_thread = native_threading.Thread(
  425             target=self._native_thread)
  426         self._event_thread.setDaemon(True)
  427         self._event_thread.start()
  428 
  429         LOG.debug("Starting green dispatch thread")
  430         utils.spawn(self._dispatch_thread)
  431 
  432     def _get_new_connection(self):
  433         # call with _wrapped_conn_lock held
  434         LOG.debug('Connecting to libvirt: %s', self._uri)
  435 
  436         # This will raise an exception on failure
  437         wrapped_conn = self._connect(self._uri, self._read_only)
  438 
  439         try:
  440             LOG.debug("Registering for lifecycle events %s", self)
  441             wrapped_conn.domainEventRegisterAny(
  442                 None,
  443                 libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
  444                 self._event_lifecycle_callback,
  445                 self)
  446         except Exception as e:
  447             LOG.warning("URI %(uri)s does not support events: %(error)s",
  448                         {'uri': self._uri, 'error': e})
  449 
  450         try:
  451             LOG.debug("Registering for connection events: %s", str(self))
  452             wrapped_conn.registerCloseCallback(self._close_callback, None)
  453         except libvirt.libvirtError as e:
  454             LOG.warning("URI %(uri)s does not support connection"
  455                         " events: %(error)s",
  456                         {'uri': self._uri, 'error': e})
  457 
  458         return wrapped_conn
  459 
  460     def _queue_conn_event_handler(self, *args, **kwargs):
  461         if self._conn_event_handler is None:
  462             return
  463 
  464         def handler():
  465             return self._conn_event_handler(*args, **kwargs)
  466 
  467         self._conn_event_handler_queue.put(handler)
  468 
  469     def _get_connection(self):
  470         # multiple concurrent connections are protected by _wrapped_conn_lock
  471         with self._wrapped_conn_lock:
  472             # Drop the existing connection if it is not usable
  473             if (self._wrapped_conn is not None and
  474                     not self._test_connection(self._wrapped_conn)):
  475                 self._wrapped_conn = None
  476                 # Connection was previously up, and went down
  477                 self._queue_conn_event_handler(
  478                     False, _('Connection to libvirt lost'))
  479 
  480             if self._wrapped_conn is None:
  481                 try:
  482                     # This will raise if it fails to get a connection
  483                     self._wrapped_conn = self._get_new_connection()
  484                 except Exception as ex:
  485                     with excutils.save_and_reraise_exception():
  486                         # If we previously had a connection and it went down,
  487                         # we generated a down event for that above.
  488                         # We also want to generate a down event for an initial
  489                         # failure, which won't be handled above.
  490                         if self._initial_connection:
  491                             self._queue_conn_event_handler(
  492                                 False,
  493                                 _('Failed to connect to libvirt: %(msg)s') %
  494                                 {'msg': ex})
  495                 finally:
  496                     self._initial_connection = False
  497 
  498                 self._queue_conn_event_handler(True, None)
  499 
  500         return self._wrapped_conn
  501 
  502     def get_connection(self):
  503         """Returns a connection to the hypervisor
  504 
  505         This method should be used to create and return a well
  506         configured connection to the hypervisor.
  507 
  508         :returns: a libvirt.virConnect object
  509         """
  510         try:
  511             conn = self._get_connection()
  512         except libvirt.libvirtError as ex:
  513             LOG.exception("Connection to libvirt failed: %s", ex)
  514             payload = {'ip': CONF.my_ip, 'method': '_connect', 'reason': ex}
  515             ctxt = nova_context.get_admin_context()
  516             rpc.get_notifier('compute').error(ctxt,
  517                                               'compute.libvirt.error',
  518                                               payload)
  519             compute_utils.notify_about_libvirt_connect_error(
  520                 ctxt, ip=CONF.my_ip, exception=ex)
  521             raise exception.HypervisorUnavailable()
  522 
  523         return conn
  524 
  525     @staticmethod
  526     def _libvirt_error_handler(context, err):
  527         # Just ignore instead of default outputting to stderr.
  528         pass
  529 
  530     def initialize(self):
  531         if self._initialized:
  532             return
  533 
  534         # NOTE(dkliban): Error handler needs to be registered before libvirt
  535         #                connection is used for the first time.  Otherwise, the
  536         #                handler does not get registered.
  537         libvirt.registerErrorHandler(self._libvirt_error_handler, None)
  538         libvirt.virEventRegisterDefaultImpl()
  539         self._init_events()
  540 
  541         LOG.debug("Starting connection event dispatch thread")
  542         utils.spawn(self._conn_event_thread)
  543 
  544         self._initialized = True
  545 
  546     def _version_check(self, lv_ver=None, hv_ver=None, hv_type=None,
  547                        op=operator.lt):
  548         """Check libvirt version, hypervisor version, and hypervisor type
  549 
  550         :param hv_type: hypervisor driver from the top of this file.
  551         """
  552         conn = self.get_connection()
  553         try:
  554             if lv_ver is not None:
  555                 libvirt_version = conn.getLibVersion()
  556                 if op(libvirt_version,
  557                       versionutils.convert_version_to_int(lv_ver)):
  558                     return False
  559 
  560             if hv_ver is not None:
  561                 hypervisor_version = conn.getVersion()
  562                 if op(hypervisor_version,
  563                       versionutils.convert_version_to_int(hv_ver)):
  564                     return False
  565 
  566             if hv_type is not None:
  567                 hypervisor_type = conn.getType()
  568                 if hypervisor_type != hv_type:
  569                     return False
  570 
  571             return True
  572         except Exception:
  573             return False
  574 
  575     def has_min_version(self, lv_ver=None, hv_ver=None, hv_type=None):
  576         return self._version_check(
  577             lv_ver=lv_ver, hv_ver=hv_ver, hv_type=hv_type, op=operator.lt)
  578 
  579     def has_version(self, lv_ver=None, hv_ver=None, hv_type=None):
  580         return self._version_check(
  581             lv_ver=lv_ver, hv_ver=hv_ver, hv_type=hv_type, op=operator.ne)
  582 
  583     def get_guest(self, instance):
  584         """Retrieve libvirt guest object for an instance.
  585 
  586         All libvirt error handling should be handled in this method and
  587         relevant nova exceptions should be raised in response.
  588 
  589         :param instance: a nova.objects.Instance object
  590 
  591         :returns: a nova.virt.libvirt.Guest object
  592         :raises exception.InstanceNotFound: The domain was not found
  593         :raises exception.InternalError: A libvirt error occurred
  594         """
  595         return libvirt_guest.Guest(self._get_domain(instance))
  596 
  597     def _get_domain(self, instance):
  598         """Retrieve libvirt domain object for an instance.
  599 
  600         All libvirt error handling should be handled in this method and
  601         relevant nova exceptions should be raised in response.
  602 
  603         :param instance: a nova.objects.Instance object
  604 
  605         :returns: a libvirt.Domain object
  606         :raises exception.InstanceNotFound: The domain was not found
  607         :raises exception.InternalError: A libvirt error occurred
  608         """
  609         try:
  610             conn = self.get_connection()
  611             return conn.lookupByUUIDString(instance.uuid)
  612         except libvirt.libvirtError as ex:
  613             error_code = ex.get_error_code()
  614             if error_code == libvirt.VIR_ERR_NO_DOMAIN:
  615                 raise exception.InstanceNotFound(instance_id=instance.uuid)
  616 
  617             msg = (_('Error from libvirt while looking up %(instance_name)s: '
  618                      '[Error Code %(error_code)s] %(ex)s') %
  619                    {'instance_name': instance.name,
  620                     'error_code': error_code,
  621                     'ex': ex})
  622             raise exception.InternalError(msg)
  623 
  624     def list_guests(self, only_running=True, only_guests=True):
  625         """Get a list of Guest objects for nova instances
  626 
  627         :param only_running: True to only return running instances
  628         :param only_guests: True to filter out any host domain (eg Dom-0)
  629 
  630         See method "list_instance_domains" for more information.
  631 
  632         :returns: list of Guest objects
  633         """
  634         return [libvirt_guest.Guest(dom) for dom in self.list_instance_domains(
  635             only_running=only_running, only_guests=only_guests)]
  636 
  637     def list_instance_domains(self, only_running=True, only_guests=True):
  638         """Get a list of libvirt.Domain objects for nova instances
  639 
  640         :param only_running: True to only return running instances
  641         :param only_guests: True to filter out any host domain (eg Dom-0)
  642 
  643         Query libvirt to a get a list of all libvirt.Domain objects
  644         that correspond to nova instances. If the only_running parameter
  645         is true this list will only include active domains, otherwise
  646         inactive domains will be included too. If the only_guests parameter
  647         is true the list will have any "host" domain (aka Xen Domain-0)
  648         filtered out.
  649 
  650         :returns: list of libvirt.Domain objects
  651         """
  652         flags = libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE
  653         if not only_running:
  654             flags = flags | libvirt.VIR_CONNECT_LIST_DOMAINS_INACTIVE
  655 
  656         # listAllDomains() returns <list of virDomain>, not <virDomain>, so
  657         # tpool.Proxy's autowrap won't catch it. We need to wrap the
  658         # contents of the list we return.
  659         alldoms = (self._wrap_libvirt_proxy(dom)
  660                    for dom in self.get_connection().listAllDomains(flags))
  661 
  662         doms = []
  663         for dom in alldoms:
  664             if only_guests and dom.ID() == 0:
  665                 continue
  666             doms.append(dom)
  667 
  668         return doms
  669 
  670     def get_online_cpus(self):
  671         """Get the set of CPUs that are online on the host
  672 
  673         :returns: set of online CPUs, raises libvirtError on error
  674         """
  675         cpus, cpu_map, online = self.get_connection().getCPUMap()
  676 
  677         online_cpus = set()
  678         for cpu in range(cpus):
  679             if cpu_map[cpu]:
  680                 online_cpus.add(cpu)
  681 
  682         return online_cpus
  683 
  684     def get_cpu_model_names(self):
  685         """Get the cpu models based on host CPU arch
  686 
  687         :returns: a list of cpu models which supported by the given CPU arch
  688         """
  689         arch = self.get_capabilities().host.cpu.arch
  690         return self.get_connection().getCPUModelNames(arch)
  691 
  692     @staticmethod
  693     def _log_host_capabilities(xmlstr):
  694         # NOTE(mriedem): This looks a bit weird but we do this so we can stub
  695         # out this method in unit/functional test runs since the xml string is
  696         # big and it can cause subunit parsing to fail (see bug 1813147).
  697         LOG.info("Libvirt host capabilities %s", xmlstr)
  698 
  699     def get_capabilities(self):
  700         """Returns the host capabilities information
  701 
  702         Returns an instance of config.LibvirtConfigCaps representing
  703         the capabilities of the host.
  704 
  705         Note: The result is cached in the member attribute _caps.
  706 
  707         :returns: a config.LibvirtConfigCaps object
  708         """
  709         if not self._caps:
  710             xmlstr = self.get_connection().getCapabilities()
  711             self._log_host_capabilities(xmlstr)
  712             self._caps = vconfig.LibvirtConfigCaps()
  713             self._caps.parse_str(xmlstr)
  714             # NOTE(mriedem): Don't attempt to get baseline CPU features
  715             # if libvirt can't determine the host cpu model.
  716             if (hasattr(libvirt,
  717                         'VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES') and
  718                     self._caps.host.cpu.model is not None):
  719                 try:
  720                     xml_str = self._caps.host.cpu.to_xml()
  721                     if isinstance(xml_str, bytes):
  722                         xml_str = xml_str.decode('utf-8')
  723                     features = self.get_connection().baselineCPU(
  724                         [xml_str],
  725                         libvirt.VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES)
  726                     if features:
  727                         cpu = vconfig.LibvirtConfigCPU()
  728                         cpu.parse_str(features)
  729                         self._caps.host.cpu.features = cpu.features
  730                 except libvirt.libvirtError as ex:
  731                     error_code = ex.get_error_code()
  732                     if error_code == libvirt.VIR_ERR_NO_SUPPORT:
  733                         LOG.warning("URI %(uri)s does not support full set"
  734                                     " of host capabilities: %(error)s",
  735                                      {'uri': self._uri, 'error': ex})
  736                     else:
  737                         raise
  738         return self._caps
  739 
  740     def get_domain_capabilities(self):
  741         """Returns the capabilities you can request when creating a
  742         domain (VM) with that hypervisor, for various combinations of
  743         architecture and machine type.
  744 
  745         In this context the fuzzy word "hypervisor" implies QEMU
  746         binary, libvirt itself and the host config.  libvirt provides
  747         this in order that callers can determine what the underlying
  748         emulator and/or libvirt is capable of, prior to creating a domain
  749         (for instance via virDomainCreateXML or virDomainDefineXML).
  750         However nova needs to know the capabilities much earlier, when
  751         the host's compute service is first initialised, in order that
  752         placement decisions can be made across many compute hosts.
  753         Therefore this is expected to be called during the init_host()
  754         phase of the driver lifecycle rather than just before booting
  755         an instance.
  756 
  757         This causes an additional complication since the Python
  758         binding for this libvirt API call requires the architecture
  759         and machine type to be provided.  So in order to gain a full
  760         picture of the hypervisor's capabilities, technically we need
  761         to call it with the right parameters, once for each
  762         (architecture, machine_type) combination which we care about.
  763         However the libvirt experts have advised us that in practice
  764         the domain capabilities do not (yet, at least) vary enough
  765         across machine types to justify the cost of calling
  766         getDomainCapabilities() once for every single (architecture,
  767         machine_type) combination.  In particular, SEV support isn't
  768         reported per-machine type, and since there are usually many
  769         machine types, we heed the advice of the experts that it's
  770         typically sufficient to call it once per host architecture:
  771 
  772             https://bugzilla.redhat.com/show_bug.cgi?id=1683471#c7
  773 
  774         However, that's not quite sufficient in the context of nova,
  775         because SEV guests typically require a q35 machine type, as do
  776         KVM/QEMU guests that want Secure Boot, whereas the current
  777         default machine type for x86_64 is 'pc'.  So we need results
  778         from the getDomainCapabilities API for at least those two.
  779         Fortunately we can take advantage of the results from the
  780         getCapabilities API which marks selected machine types as
  781         canonical, e.g.:
  782 
  783             <machine canonical='pc-i440fx-2.11' maxCpus='255'>pc</machine>
  784             <machine canonical='pc-q35-2.11' maxCpus='288'>q35</machine>
  785 
  786         So for now, we call getDomainCapabilities for these canonical
  787         machine types of each architecture, plus for the
  788         architecture's default machine type, if that is not one of the
  789         canonical types.
  790 
  791         Future domain capabilities might report SEV in a more
  792         fine-grained manner, and we also expect to use this method to
  793         detect other features, such as for gracefully handling machine
  794         types and potentially for detecting OVMF binaries.  Therefore
  795         we memoize the results of the API calls in a nested dict where
  796         the top-level keys are architectures, and second-level keys
  797         are machine types, in order to allow easy expansion later.
  798 
  799         Whenever libvirt/QEMU are updated, cached domCapabilities
  800         would get outdated (because QEMU will contain new features and
  801         the capabilities will vary).  However, this should not be a
  802         problem here, because when libvirt/QEMU gets updated, the
  803         nova-compute agent also needs restarting, at which point the
  804         memoization will vanish because it's not persisted to disk.
  805 
  806         Note: The result is cached in the member attribute
  807         _domain_caps.
  808 
  809         :returns: a nested dict of dicts which maps architectures to
  810         machine types to instances of config.LibvirtConfigDomainCaps
  811         representing the domain capabilities of the host for that arch
  812         and machine type:
  813 
  814         { arch:
  815           { machine_type: LibvirtConfigDomainCaps }
  816         }
  817         """
  818         if self._domain_caps:
  819             return self._domain_caps
  820 
  821         domain_caps: ty.Dict = defaultdict(dict)
  822         caps = self.get_capabilities()
  823         virt_type = CONF.libvirt.virt_type
  824 
  825         for guest in caps.guests:
  826             arch = guest.arch
  827             domain = guest.domains.get(virt_type, guest.default_domain)
  828 
  829             for machine_type in self._get_machine_types(arch, domain):
  830                 # It is expected that if there are multiple <guest>
  831                 # elements, each will have a different architecture;
  832                 # for example, on x86 hosts one <guest> will contain
  833                 # <arch name='i686'> and one will contain <arch
  834                 # name='x86_64'>. But it doesn't hurt to add a safety
  835                 # net to avoid needlessly calling libvirt's API more
  836                 # times than we need.
  837                 if machine_type and machine_type in domain_caps[arch]:
  838                     continue
  839                 self._add_to_domain_capabilities(domain.emulator, arch,
  840                                                  domain_caps, machine_type,
  841                                                  virt_type)
  842 
  843         # NOTE(aspiers): Use a temporary variable to update the
  844         # instance variable atomically, otherwise if some API
  845         # calls succeeded and then one failed, we might
  846         # accidentally memoize a partial result.
  847         self._domain_caps = domain_caps
  848 
  849         return self._domain_caps
  850 
  851     def _get_machine_types(self, arch, domain):
  852         """Get the machine types for this architecture for which we need to
  853         call getDomainCapabilities, i.e. the canonical machine types,
  854         and the default machine type (if it's not one of the canonical
  855         machine types).
  856 
  857         See the docstring for get_domain_capabilities() for an explanation
  858         of why we choose this set of machine types.
  859         """
  860         # NOTE(aspiers): machine_type could be None here if nova
  861         # doesn't have a default machine type for this architecture.
  862         # See _add_to_domain_capabilities() below for how this is handled.
  863         mtypes = set([libvirt_utils.get_default_machine_type(arch)])
  864         mtypes.update(domain.aliases.keys())
  865         LOG.debug("Getting domain capabilities for %(arch)s via "
  866                   "machine types: %(mtypes)s",
  867                   {'arch': arch, 'mtypes': mtypes})
  868         return mtypes
  869 
  870     def _add_to_domain_capabilities(self, emulator_bin, arch, domain_caps,
  871                                     machine_type, virt_type):
  872         # NOTE(aspiers): machine_type could be None here if nova
  873         # doesn't have a default machine type for this architecture.
  874         # In that case we pass a machine_type of None to the libvirt
  875         # API and rely on it choosing a sensible default which will be
  876         # returned in the <machine> element.  It could also be an
  877         # alias like 'pc' rather than a full machine type.
  878         #
  879         # NOTE(kchamart): Prior to libvirt v4.7.0 libvirt picked its
  880         # default machine type for x86, 'pc', as reported by QEMU's
  881         # default.  From libvirt v4.7.0 onwards, libvirt _explicitly_
  882         # declared the "preferred" default for x86 as 'pc' (and
  883         # appropriate values for other architectures), and only uses
  884         # QEMU's reported default (whatever that may be) if 'pc' does
  885         # not exist.  This was done "to isolate applications from
  886         # hypervisor changes that may cause incompatibilities" --
  887         # i.e. if, or when, QEMU changes its default machine type to
  888         # something else.  Refer to this libvirt commit:
  889         #
  890         #   https://libvirt.org/git/?p=libvirt.git;a=commit;h=26cfb1a3
  891         try:
  892             cap_obj = self._get_domain_capabilities(
  893                 emulator_bin=emulator_bin, arch=arch,
  894                 machine_type=machine_type, virt_type=virt_type)
  895         except libvirt.libvirtError as ex:
  896             # NOTE(sean-k-mooney): This can happen for several
  897             # reasons, but one common example is if you have
  898             # multiple QEMU emulators installed and you set
  899             # virt-type=kvm. In this case any non-native emulator,
  900             # e.g. AArch64 on an x86 host, will (correctly) raise
  901             # an exception as KVM cannot be used to accelerate CPU
  902             # instructions for non-native architectures.
  903             error_code = ex.get_error_code()
  904             LOG.debug(
  905                 "Error from libvirt when retrieving domain capabilities "
  906                 "for arch %(arch)s / virt_type %(virt_type)s / "
  907                 "machine_type %(mach_type)s: "
  908                 "[Error Code %(error_code)s]: %(exception)s",
  909                 {'arch': arch, 'virt_type': virt_type,
  910                  'mach_type': machine_type, 'error_code': error_code,
  911                  'exception': ex})
  912             # Remove archs added by default dict lookup when checking
  913             # if the machine type has already been recoded.
  914             if arch in domain_caps:
  915                 domain_caps.pop(arch)
  916             return
  917 
  918         # Register the domain caps using the expanded form of
  919         # machine type returned by libvirt in the <machine>
  920         # element (e.g. pc-i440fx-2.11)
  921         if cap_obj.machine_type:
  922             domain_caps[arch][cap_obj.machine_type] = cap_obj
  923         else:
  924             # NOTE(aspiers): In theory this should never happen,
  925             # but better safe than sorry.
  926             LOG.warning(
  927                 "libvirt getDomainCapabilities("
  928                 "emulator_bin=%(emulator_bin)s, arch=%(arch)s, "
  929                 "machine_type=%(machine_type)s, virt_type=%(virt_type)s) "
  930                 "returned null <machine> type",
  931                 {'emulator_bin': emulator_bin, 'arch': arch,
  932                  'machine_type': machine_type, 'virt_type': virt_type}
  933             )
  934 
  935         # And if we passed an alias, register the domain caps
  936         # under that too.
  937         if machine_type and machine_type != cap_obj.machine_type:
  938             domain_caps[arch][machine_type] = cap_obj
  939             cap_obj.machine_type_alias = machine_type
  940 
  941     def _get_domain_capabilities(self, emulator_bin=None, arch=None,
  942                                  machine_type=None, virt_type=None, flags=0):
  943         xmlstr = self.get_connection().getDomainCapabilities(
  944             emulator_bin,
  945             arch,
  946             machine_type,
  947             virt_type,
  948             flags
  949         )
  950         LOG.debug("Libvirt host hypervisor capabilities for arch=%s and "
  951                   "machine_type=%s:\n%s", arch, machine_type, xmlstr)
  952         caps = vconfig.LibvirtConfigDomainCaps()
  953         caps.parse_str(xmlstr)
  954         return caps
  955 
  956     def get_driver_type(self):
  957         """Get hypervisor type.
  958 
  959         :returns: hypervisor type (ex. qemu)
  960 
  961         """
  962 
  963         return self.get_connection().getType()
  964 
  965     def get_version(self):
  966         """Get hypervisor version.
  967 
  968         :returns: hypervisor version (ex. 12003)
  969 
  970         """
  971 
  972         return self.get_connection().getVersion()
  973 
  974     def get_hostname(self):
  975         """Returns the hostname of the hypervisor."""
  976         hostname = self.get_connection().getHostname()
  977         if self._hostname is None:
  978             self._hostname = hostname
  979         elif hostname != self._hostname:
  980             LOG.error('Hostname has changed from %(old)s '
  981                       'to %(new)s. A restart is required to take effect.',
  982                       {'old': self._hostname, 'new': hostname})
  983         return self._hostname
  984 
  985     def find_secret(self, usage_type, usage_id):
  986         """Find a secret.
  987 
  988         usage_type: one of 'iscsi', 'ceph', 'rbd' or 'volume'
  989         usage_id: name of resource in secret
  990         """
  991         if usage_type == 'iscsi':
  992             usage_type_const = libvirt.VIR_SECRET_USAGE_TYPE_ISCSI
  993         elif usage_type in ('rbd', 'ceph'):
  994             usage_type_const = libvirt.VIR_SECRET_USAGE_TYPE_CEPH
  995         elif usage_type == 'volume':
  996             usage_type_const = libvirt.VIR_SECRET_USAGE_TYPE_VOLUME
  997         else:
  998             msg = _("Invalid usage_type: %s")
  999             raise exception.InternalError(msg % usage_type)
 1000 
 1001         try:
 1002             conn = self.get_connection()
 1003             return conn.secretLookupByUsage(usage_type_const, usage_id)
 1004         except libvirt.libvirtError as e:
 1005             if e.get_error_code() == libvirt.VIR_ERR_NO_SECRET:
 1006                 return None
 1007 
 1008     def create_secret(self, usage_type, usage_id, password=None, uuid=None):
 1009         """Create a secret.
 1010 
 1011         :param usage_type: one of 'iscsi', 'ceph', 'rbd', 'volume', 'vtpm'.
 1012                            'rbd' will be converted to 'ceph'. 'vtpm' secrets
 1013                            are private and ephemeral; others are not.
 1014         :param usage_id: name of resource in secret
 1015         :param password: optional secret value to set
 1016         :param uuid: optional UUID of the secret; else one is generated by
 1017             libvirt
 1018         """
 1019         secret_conf = vconfig.LibvirtConfigSecret()
 1020         secret_conf.ephemeral = usage_type == 'vtpm'
 1021         secret_conf.private = usage_type == 'vtpm'
 1022         secret_conf.usage_id = usage_id
 1023         secret_conf.uuid = uuid
 1024         if usage_type in ('rbd', 'ceph'):
 1025             secret_conf.usage_type = 'ceph'
 1026         elif usage_type == 'iscsi':
 1027             secret_conf.usage_type = 'iscsi'
 1028         elif usage_type == 'volume':
 1029             secret_conf.usage_type = 'volume'
 1030         elif usage_type == 'vtpm':
 1031             secret_conf.usage_type = 'vtpm'
 1032         else:
 1033             msg = _("Invalid usage_type: %s")
 1034             raise exception.InternalError(msg % usage_type)
 1035 
 1036         xml = secret_conf.to_xml()
 1037         try:
 1038             LOG.debug('Secret XML: %s', xml)
 1039             conn = self.get_connection()
 1040             secret = conn.secretDefineXML(xml)
 1041             if password is not None:
 1042                 secret.setValue(password)
 1043             return secret
 1044         except libvirt.libvirtError:
 1045             with excutils.save_and_reraise_exception():
 1046                 LOG.error('Error defining a secret with XML: %s', xml)
 1047 
 1048     def delete_secret(self, usage_type, usage_id):
 1049         """Delete a secret.
 1050 
 1051         :param usage_type: one of 'iscsi', 'ceph', 'rbd', 'volume' or 'vtpm'
 1052         :param usage_id: name of resource in secret
 1053         """
 1054         secret = self.find_secret(usage_type, usage_id)
 1055         if secret is not None:
 1056             secret.undefine()
 1057 
 1058     def _get_hardware_info(self):
 1059         """Returns hardware information about the Node.
 1060 
 1061         Note that the memory size is reported in MiB instead of KiB.
 1062         """
 1063         return self.get_connection().getInfo()
 1064 
 1065     def get_memory_mb_total(self):
 1066         """Get the total memory size(MB) of physical computer.
 1067 
 1068         :returns: the total amount of memory(MB).
 1069         """
 1070         if CONF.libvirt.file_backed_memory > 0:
 1071             return CONF.libvirt.file_backed_memory
 1072         else:
 1073             return self._get_hardware_info()[1]
 1074 
 1075     def _sum_domain_memory_mb(self, include_host=True):
 1076         """Get the total memory consumed by guest domains
 1077 
 1078         If include_host is True, subtract available host memory from guest 0
 1079         to get real used memory within dom0 within xen
 1080         """
 1081         used = 0
 1082         for guest in self.list_guests(only_guests=False):
 1083             try:
 1084                 # TODO(sahid): Use get_info...
 1085                 dom_mem = int(guest._get_domain_info()[2])
 1086             except libvirt.libvirtError as e:
 1087                 LOG.warning("couldn't obtain the memory from domain:"
 1088                             " %(uuid)s, exception: %(ex)s",
 1089                             {"uuid": guest.uuid, "ex": e})
 1090                 continue
 1091             if include_host and guest.id == 0:
 1092                 # Memory usage for the host domain (dom0 in xen) is the
 1093                 # reported memory minus available memory
 1094                 used += (dom_mem - self._get_avail_memory_kb())
 1095             else:
 1096                 used += dom_mem
 1097         # Convert it to MB
 1098         return used // units.Ki
 1099 
 1100     @staticmethod
 1101     def _get_avail_memory_kb():
 1102         with open('/proc/meminfo') as fp:
 1103             m = fp.read().split()
 1104         idx1 = m.index('MemFree:')
 1105         idx2 = m.index('Buffers:')
 1106         idx3 = m.index('Cached:')
 1107 
 1108         avail = int(m[idx1 + 1]) + int(m[idx2 + 1]) + int(m[idx3 + 1])
 1109 
 1110         return avail
 1111 
 1112     def get_memory_mb_used(self):
 1113         """Get the used memory size(MB) of physical computer.
 1114 
 1115         :returns: the total usage of memory(MB).
 1116         """
 1117         if CONF.libvirt.virt_type == 'xen':
 1118             # For xen, report the sum of all domains, with
 1119             return self._sum_domain_memory_mb(include_host=True)
 1120         elif CONF.libvirt.file_backed_memory > 0:
 1121             # For file_backed_memory, report the total usage of guests,
 1122             # ignoring host memory
 1123             return self._sum_domain_memory_mb(include_host=False)
 1124         else:
 1125             return (self.get_memory_mb_total() -
 1126                    (self._get_avail_memory_kb() // units.Ki))
 1127 
 1128     def get_cpu_stats(self):
 1129         """Returns the current CPU state of the host with frequency."""
 1130         stats = self.get_connection().getCPUStats(
 1131             libvirt.VIR_NODE_CPU_STATS_ALL_CPUS, 0)
 1132         # getInfo() returns various information about the host node
 1133         # No. 3 is the expected CPU frequency.
 1134         stats["frequency"] = self._get_hardware_info()[3]
 1135         return stats
 1136 
 1137     def write_instance_config(self, xml):
 1138         """Defines a domain, but does not start it.
 1139 
 1140         :param xml: XML domain definition of the guest.
 1141 
 1142         :returns: an instance of Guest
 1143         """
 1144         domain = self.get_connection().defineXML(xml)
 1145         return libvirt_guest.Guest(domain)
 1146 
 1147     def device_lookup_by_name(self, name):
 1148         """Lookup a node device by its name.
 1149 
 1150 
 1151         :returns: a virNodeDevice instance
 1152         """
 1153         return self.get_connection().nodeDeviceLookupByName(name)
 1154 
 1155     def list_pci_devices(self, flags=0):
 1156         """Lookup pci devices.
 1157 
 1158         :returns: a list of virNodeDevice instance
 1159         """
 1160         return self._list_devices("pci", flags=flags)
 1161 
 1162     def list_mdev_capable_devices(self, flags=0):
 1163         """Lookup devices supporting mdev capabilities.
 1164 
 1165         :returns: a list of virNodeDevice instance
 1166         """
 1167         return self._list_devices("mdev_types", flags=flags)
 1168 
 1169     def list_mediated_devices(self, flags=0):
 1170         """Lookup mediated devices.
 1171 
 1172         :returns: a list of virNodeDevice instance
 1173         """
 1174         return self._list_devices("mdev", flags=flags)
 1175 
 1176     def _list_devices(self, cap, flags=0):
 1177         """Lookup devices.
 1178 
 1179         :returns: a list of virNodeDevice instance
 1180         """
 1181         try:
 1182             return self.get_connection().listDevices(cap, flags)
 1183         except libvirt.libvirtError as ex:
 1184             error_code = ex.get_error_code()
 1185             if error_code == libvirt.VIR_ERR_NO_SUPPORT:
 1186                 LOG.warning("URI %(uri)s does not support "
 1187                             "listDevices: %(error)s",
 1188                             {'uri': self._uri, 'error': ex})
 1189                 return []
 1190             else:
 1191                 raise
 1192 
 1193     def list_all_devices(
 1194             self, flags: int = 0) -> ty.List['libvirt.virNodeDevice']:
 1195         """Lookup devices.
 1196 
 1197         :param flags: a bitmask of flags to filter the returned devices.
 1198         :returns: a list of virNodeDevice xml strings.
 1199         """
 1200         try:
 1201             return self.get_connection().listAllDevices(flags) or []
 1202         except libvirt.libvirtError as ex:
 1203             LOG.warning(ex)
 1204             return []
 1205 
 1206     def compare_cpu(self, xmlDesc, flags=0):
 1207         """Compares the given CPU description with the host CPU."""
 1208         return self.get_connection().compareCPU(xmlDesc, flags)
 1209 
 1210     def is_cpu_control_policy_capable(self):
 1211         """Returns whether kernel configuration CGROUP_SCHED is enabled
 1212 
 1213         CONFIG_CGROUP_SCHED may be disabled in some kernel configs to
 1214         improve scheduler latency.
 1215         """
 1216         try:
 1217             with open("/proc/self/mounts", "r") as fd:
 1218                 for line in fd.readlines():
 1219                     # mount options and split options
 1220                     bits = line.split()[3].split(",")
 1221                     if "cpu" in bits:
 1222                         return True
 1223                 return False
 1224         except IOError:
 1225             return False
 1226 
 1227     @property
 1228     def has_hyperthreading(self):
 1229         """Determine if host CPU has SMT, a.k.a. HyperThreading.
 1230 
 1231         :return: True if the host has SMT enabled, else False.
 1232         """
 1233         if self._has_hyperthreading is not None:
 1234             return self._has_hyperthreading
 1235 
 1236         self._has_hyperthreading = False
 1237 
 1238         # we don't use '/capabilities/host/cpu/topology' since libvirt doesn't
 1239         # guarantee the accuracy of this information
 1240         for cell in self.get_capabilities().host.topology.cells:
 1241             if any(len(cpu.siblings) > 1 for cpu in cell.cpus if cpu.siblings):
 1242                 self._has_hyperthreading = True
 1243                 break
 1244 
 1245         return self._has_hyperthreading
 1246 
 1247     def _kernel_supports_amd_sev(self):
 1248         if not os.path.exists(SEV_KERNEL_PARAM_FILE):
 1249             LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)
 1250             return False
 1251 
 1252         with open(SEV_KERNEL_PARAM_FILE) as f:
 1253             contents = f.read()
 1254             LOG.debug("%s contains [%s]", SEV_KERNEL_PARAM_FILE, contents)
 1255             return contents == "1\n"
 1256 
 1257     @property
 1258     def supports_amd_sev(self):
 1259         """Returns a boolean indicating whether AMD SEV (Secure Encrypted
 1260         Virtualization) is supported.  This is conditional on support
 1261         in the hardware, kernel, qemu, and libvirt.
 1262 
 1263         The result is memoized, since it is not expected to change
 1264         during the lifetime of a running nova-compute service; if the
 1265         hypervisor stack is changed or reconfigured in a way which
 1266         would affect the support, nova-compute should be restarted
 1267         anyway.
 1268         """
 1269         if self._supports_amd_sev is None:
 1270             self._set_amd_sev_support()
 1271         return self._supports_amd_sev
 1272 
 1273     def _set_amd_sev_support(self):
 1274         self._supports_amd_sev = False
 1275 
 1276         if not self._kernel_supports_amd_sev():
 1277             LOG.info("kernel doesn't support AMD SEV")
 1278             self._supports_amd_sev = False
 1279             return
 1280 
 1281         domain_caps = self.get_domain_capabilities()
 1282         for arch in domain_caps:
 1283             for machine_type in domain_caps[arch]:
 1284                 LOG.debug("Checking SEV support for arch %s "
 1285                           "and machine type %s", arch, machine_type)
 1286                 for feature in domain_caps[arch][machine_type].features:
 1287                     feature_is_sev = isinstance(
 1288                         feature, vconfig.LibvirtConfigDomainCapsFeatureSev)
 1289                     if (feature_is_sev and feature.supported):
 1290                         LOG.info("AMD SEV support detected")
 1291                         self._supports_amd_sev = True
 1292                         return
 1293 
 1294         LOG.debug("No AMD SEV support detected for any (arch, machine_type)")