Fixes hypervisor based image filtering on Hyper-V
Fixes Bug #1073547 Nova scheduler's ImagePropertiesFilter based filtering requires the "supported_instances" stat returned by the driver's get_host_stats method. This fix implements the get_host_stats method, providing also some refactoring in the process by adding a HostOps class. Change-Id: Ia6e73eb3e8ff0be028854fe4fd6d8e305fa1d504
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -103,6 +103,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
|
|||||||
|
|
||||||
# Modules in which the mocks are going to be injected
|
# Modules in which the mocks are going to be injected
|
||||||
from nova.virt.hyperv import baseops
|
from nova.virt.hyperv import baseops
|
||||||
|
from nova.virt.hyperv import hostops
|
||||||
from nova.virt.hyperv import livemigrationops
|
from nova.virt.hyperv import livemigrationops
|
||||||
from nova.virt.hyperv import snapshotops
|
from nova.virt.hyperv import snapshotops
|
||||||
from nova.virt.hyperv import vmops
|
from nova.virt.hyperv import vmops
|
||||||
@@ -112,6 +113,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
|
|||||||
modules_to_test = [
|
modules_to_test = [
|
||||||
driver_hyperv,
|
driver_hyperv,
|
||||||
baseops,
|
baseops,
|
||||||
|
hostops,
|
||||||
vmops,
|
vmops,
|
||||||
vmutils,
|
vmutils,
|
||||||
volumeops,
|
volumeops,
|
||||||
@@ -157,6 +159,14 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
|
|||||||
|
|
||||||
self.assertEquals(dic['hypervisor_hostname'], platform.node())
|
self.assertEquals(dic['hypervisor_hostname'], platform.node())
|
||||||
|
|
||||||
|
def test_get_host_stats(self):
|
||||||
|
dic = self._conn.get_host_stats(True)
|
||||||
|
|
||||||
|
self.assertEquals(dic['disk_total'],
|
||||||
|
dic['disk_used'] + dic['disk_available'])
|
||||||
|
self.assertEquals(dic['host_memory_total'],
|
||||||
|
dic['host_memory_overhead'] + dic['host_memory_free'])
|
||||||
|
|
||||||
def test_list_instances(self):
|
def test_list_instances(self):
|
||||||
num_vms = self._hypervutils.get_vm_count()
|
num_vms = self._hypervutils.get_vm_count()
|
||||||
instances = self._conn.list_instances()
|
instances = self._conn.list_instances()
|
||||||
|
@@ -63,6 +63,7 @@ Using the Python WMI library:
|
|||||||
|
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.virt import driver
|
from nova.virt import driver
|
||||||
|
from nova.virt.hyperv import hostops
|
||||||
from nova.virt.hyperv import livemigrationops
|
from nova.virt.hyperv import livemigrationops
|
||||||
from nova.virt.hyperv import snapshotops
|
from nova.virt.hyperv import snapshotops
|
||||||
from nova.virt.hyperv import vmops
|
from nova.virt.hyperv import vmops
|
||||||
@@ -75,6 +76,7 @@ class HyperVDriver(driver.ComputeDriver):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(HyperVDriver, self).__init__()
|
super(HyperVDriver, self).__init__()
|
||||||
|
|
||||||
|
self._hostops = hostops.HostOps()
|
||||||
self._volumeops = volumeops.VolumeOps()
|
self._volumeops = volumeops.VolumeOps()
|
||||||
self._vmops = vmops.VMOps(self._volumeops)
|
self._vmops = vmops.VMOps(self._volumeops)
|
||||||
self._snapshotops = snapshotops.SnapshotOps()
|
self._snapshotops = snapshotops.SnapshotOps()
|
||||||
@@ -121,15 +123,13 @@ class HyperVDriver(driver.ComputeDriver):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_available_resource(self):
|
def get_available_resource(self):
|
||||||
return self._vmops.get_available_resource()
|
return self._hostops.get_available_resource()
|
||||||
|
|
||||||
def get_host_stats(self, refresh=False):
|
def get_host_stats(self, refresh=False):
|
||||||
"""See xenapi_conn.py implementation."""
|
return self._hostops.get_host_stats(refresh)
|
||||||
return {}
|
|
||||||
|
|
||||||
def host_power_action(self, host, action):
|
def host_power_action(self, host, action):
|
||||||
"""Reboots, shuts down or powers up the host."""
|
return self._hostops.host_power_action(host, action)
|
||||||
pass
|
|
||||||
|
|
||||||
def set_host_enabled(self, host, enabled):
|
def set_host_enabled(self, host, enabled):
|
||||||
"""Sets the specified host's ability to accept new instances."""
|
"""Sets the specified host's ability to accept new instances."""
|
||||||
|
169
nova/virt/hyperv/hostops.py
Normal file
169
nova/virt/hyperv/hostops.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2012 Cloudbase Solutions Srl
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Management class for host operations.
|
||||||
|
"""
|
||||||
|
import multiprocessing
|
||||||
|
import platform
|
||||||
|
|
||||||
|
from nova.openstack.common import log as logging
|
||||||
|
from nova.virt.hyperv import baseops
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HostOps(baseops.BaseOps):
|
||||||
|
def __init__(self):
|
||||||
|
super(HostOps, self).__init__()
|
||||||
|
self._stats = None
|
||||||
|
|
||||||
|
def _get_vcpu_total(self):
|
||||||
|
"""Get vcpu number of physical computer.
|
||||||
|
:returns: the number of cpu core.
|
||||||
|
"""
|
||||||
|
# On certain platforms, this will raise a NotImplementedError.
|
||||||
|
try:
|
||||||
|
return multiprocessing.cpu_count()
|
||||||
|
except NotImplementedError:
|
||||||
|
LOG.warn(_("Cannot get the number of cpu, because this "
|
||||||
|
"function is not implemented for this platform. "
|
||||||
|
"This error can be safely ignored for now."))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _get_memory_mb_total(self):
|
||||||
|
"""Get the total memory size(MB) of physical computer.
|
||||||
|
:returns: the total amount of memory(MB).
|
||||||
|
"""
|
||||||
|
total_kb = self._conn_cimv2.query(
|
||||||
|
"SELECT TotalVisibleMemorySize FROM win32_operatingsystem")[0]\
|
||||||
|
.TotalVisibleMemorySize
|
||||||
|
total_mb = long(total_kb) / 1024
|
||||||
|
return total_mb
|
||||||
|
|
||||||
|
def _get_local_gb_total(self):
|
||||||
|
"""Get the total hdd size(GB) of physical computer.
|
||||||
|
:returns:
|
||||||
|
The total amount of HDD(GB).
|
||||||
|
Note that this value shows a partition where
|
||||||
|
NOVA-INST-DIR/instances mounts.
|
||||||
|
"""
|
||||||
|
#TODO(jordanrinke): This binds to C only right now,
|
||||||
|
#need to bind to instance dir
|
||||||
|
total_kb = self._conn_cimv2.query(
|
||||||
|
"SELECT Size FROM win32_logicaldisk WHERE DriveType=3")[0].Size
|
||||||
|
total_gb = long(total_kb) / (1024 ** 3)
|
||||||
|
return total_gb
|
||||||
|
|
||||||
|
def _get_vcpu_used(self):
|
||||||
|
""" Get vcpu usage number of physical computer.
|
||||||
|
:returns: The total number of vcpu that currently used.
|
||||||
|
"""
|
||||||
|
#TODO(jordanrinke) figure out a way to count assigned VCPUs
|
||||||
|
total_vcpu = 0
|
||||||
|
return total_vcpu
|
||||||
|
|
||||||
|
def _get_memory_mb_used(self):
|
||||||
|
"""Get the free memory size(MB) of physical computer.
|
||||||
|
:returns: the total usage of memory(MB).
|
||||||
|
"""
|
||||||
|
total_kb = self._conn_cimv2.query(
|
||||||
|
"SELECT FreePhysicalMemory FROM win32_operatingsystem")[0]\
|
||||||
|
.FreePhysicalMemory
|
||||||
|
total_mb = long(total_kb) / 1024
|
||||||
|
|
||||||
|
return total_mb
|
||||||
|
|
||||||
|
def _get_local_gb_used(self):
|
||||||
|
"""Get the free hdd size(GB) of physical computer.
|
||||||
|
:returns:
|
||||||
|
The total usage of HDD(GB).
|
||||||
|
Note that this value shows a partition where
|
||||||
|
NOVA-INST-DIR/instances mounts.
|
||||||
|
"""
|
||||||
|
#TODO(jordanrinke): This binds to C only right now,
|
||||||
|
#need to bind to instance dir
|
||||||
|
total_kb = self._conn_cimv2.query(
|
||||||
|
"SELECT FreeSpace FROM win32_logicaldisk WHERE DriveType=3")[0]\
|
||||||
|
.FreeSpace
|
||||||
|
total_gb = long(total_kb) / (1024 ** 3)
|
||||||
|
return total_gb
|
||||||
|
|
||||||
|
def _get_hypervisor_version(self):
|
||||||
|
"""Get hypervisor version.
|
||||||
|
:returns: hypervisor version (ex. 12003)
|
||||||
|
"""
|
||||||
|
version = self._conn_cimv2.Win32_OperatingSystem()[0]\
|
||||||
|
.Version.replace('.', '')
|
||||||
|
LOG.info(_('Windows version: %s ') % version)
|
||||||
|
return version
|
||||||
|
|
||||||
|
def get_available_resource(self):
|
||||||
|
"""Retrieve resource info.
|
||||||
|
|
||||||
|
This method is called when nova-compute launches, and
|
||||||
|
as part of a periodic task.
|
||||||
|
|
||||||
|
:returns: dictionary describing resources
|
||||||
|
|
||||||
|
"""
|
||||||
|
LOG.info(_('get_available_resource called'))
|
||||||
|
|
||||||
|
# TODO(alexpilotti) implemented cpu_info
|
||||||
|
dic = {'vcpus': self._get_vcpu_total(),
|
||||||
|
'memory_mb': self._get_memory_mb_total(),
|
||||||
|
'local_gb': self._get_local_gb_total(),
|
||||||
|
'vcpus_used': self._get_vcpu_used(),
|
||||||
|
'memory_mb_used': self._get_memory_mb_used(),
|
||||||
|
'local_gb_used': self._get_local_gb_used(),
|
||||||
|
'hypervisor_type': "hyperv",
|
||||||
|
'hypervisor_version': self._get_hypervisor_version(),
|
||||||
|
'hypervisor_hostname': platform.node(),
|
||||||
|
'cpu_info': 'unknown'}
|
||||||
|
|
||||||
|
return dic
|
||||||
|
|
||||||
|
def _update_stats(self):
|
||||||
|
LOG.debug(_("Updating host stats"))
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data["disk_total"] = self._get_local_gb_total()
|
||||||
|
data["disk_used"] = self._get_local_gb_used()
|
||||||
|
data["disk_available"] = data["disk_total"] - data["disk_used"]
|
||||||
|
data["host_memory_total"] = self._get_memory_mb_total()
|
||||||
|
data["host_memory_overhead"] = self._get_memory_mb_used()
|
||||||
|
data["host_memory_free"] = \
|
||||||
|
data["host_memory_total"] - data["host_memory_overhead"]
|
||||||
|
data["host_memory_free_computed"] = data["host_memory_free"]
|
||||||
|
data["supported_instances"] = \
|
||||||
|
[('i686', 'hyperv', 'hvm'),
|
||||||
|
('x86_64', 'hyperv', 'hvm')]
|
||||||
|
|
||||||
|
self._stats = data
|
||||||
|
|
||||||
|
def get_host_stats(self, refresh=False):
|
||||||
|
"""Return the current state of the host. If 'refresh' is
|
||||||
|
True, run the update first."""
|
||||||
|
LOG.info(_("get_host_stats called"))
|
||||||
|
|
||||||
|
if refresh or not self._stats:
|
||||||
|
self._update_stats()
|
||||||
|
return self._stats
|
||||||
|
|
||||||
|
def host_power_action(self, host, action):
|
||||||
|
"""Reboots, shuts down or powers up the host."""
|
||||||
|
pass
|
@@ -18,9 +18,7 @@
|
|||||||
"""
|
"""
|
||||||
Management class for basic VM operations.
|
Management class for basic VM operations.
|
||||||
"""
|
"""
|
||||||
import multiprocessing
|
|
||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from nova import db
|
from nova import db
|
||||||
@@ -475,111 +473,6 @@ class VMOps(baseops.BaseOps):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise vmutils.HyperVException(msg)
|
raise vmutils.HyperVException(msg)
|
||||||
|
|
||||||
def _get_vcpu_total(self):
|
|
||||||
"""Get vcpu number of physical computer.
|
|
||||||
:returns: the number of cpu core.
|
|
||||||
"""
|
|
||||||
# On certain platforms, this will raise a NotImplementedError.
|
|
||||||
try:
|
|
||||||
return multiprocessing.cpu_count()
|
|
||||||
except NotImplementedError:
|
|
||||||
LOG.warn(_("Cannot get the number of cpu, because this "
|
|
||||||
"function is not implemented for this platform. "
|
|
||||||
"This error can be safely ignored for now."))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def _get_memory_mb_total(self):
|
|
||||||
"""Get the total memory size(MB) of physical computer.
|
|
||||||
:returns: the total amount of memory(MB).
|
|
||||||
"""
|
|
||||||
total_kb = self._conn_cimv2.query(
|
|
||||||
"SELECT TotalVisibleMemorySize FROM win32_operatingsystem")[0]\
|
|
||||||
.TotalVisibleMemorySize
|
|
||||||
total_mb = long(total_kb) / 1024
|
|
||||||
return total_mb
|
|
||||||
|
|
||||||
def _get_local_gb_total(self):
|
|
||||||
"""Get the total hdd size(GB) of physical computer.
|
|
||||||
:returns:
|
|
||||||
The total amount of HDD(GB).
|
|
||||||
Note that this value shows a partition where
|
|
||||||
NOVA-INST-DIR/instances mounts.
|
|
||||||
"""
|
|
||||||
#TODO(jordanrinke): This binds to C only right now,
|
|
||||||
#need to bind to instance dir
|
|
||||||
total_kb = self._conn_cimv2.query(
|
|
||||||
"SELECT Size FROM win32_logicaldisk WHERE DriveType=3")[0].Size
|
|
||||||
total_gb = long(total_kb) / (1024 ** 3)
|
|
||||||
return total_gb
|
|
||||||
|
|
||||||
def _get_vcpu_used(self):
|
|
||||||
""" Get vcpu usage number of physical computer.
|
|
||||||
:returns: The total number of vcpu that currently used.
|
|
||||||
"""
|
|
||||||
#TODO(jordanrinke) figure out a way to count assigned VCPUs
|
|
||||||
total_vcpu = 0
|
|
||||||
return total_vcpu
|
|
||||||
|
|
||||||
def _get_memory_mb_used(self):
|
|
||||||
"""Get the free memory size(MB) of physical computer.
|
|
||||||
:returns: the total usage of memory(MB).
|
|
||||||
"""
|
|
||||||
total_kb = self._conn_cimv2.query(
|
|
||||||
"SELECT FreePhysicalMemory FROM win32_operatingsystem")[0]\
|
|
||||||
.FreePhysicalMemory
|
|
||||||
total_mb = long(total_kb) / 1024
|
|
||||||
|
|
||||||
return total_mb
|
|
||||||
|
|
||||||
def _get_local_gb_used(self):
|
|
||||||
"""Get the free hdd size(GB) of physical computer.
|
|
||||||
:returns:
|
|
||||||
The total usage of HDD(GB).
|
|
||||||
Note that this value shows a partition where
|
|
||||||
NOVA-INST-DIR/instances mounts.
|
|
||||||
"""
|
|
||||||
#TODO(jordanrinke): This binds to C only right now,
|
|
||||||
#need to bind to instance dir
|
|
||||||
total_kb = self._conn_cimv2.query(
|
|
||||||
"SELECT FreeSpace FROM win32_logicaldisk WHERE DriveType=3")[0]\
|
|
||||||
.FreeSpace
|
|
||||||
total_gb = long(total_kb) / (1024 ** 3)
|
|
||||||
return total_gb
|
|
||||||
|
|
||||||
def _get_hypervisor_version(self):
|
|
||||||
"""Get hypervisor version.
|
|
||||||
:returns: hypervisor version (ex. 12003)
|
|
||||||
"""
|
|
||||||
version = self._conn_cimv2.Win32_OperatingSystem()[0]\
|
|
||||||
.Version.replace('.', '')
|
|
||||||
LOG.info(_('Windows version: %s ') % version)
|
|
||||||
return version
|
|
||||||
|
|
||||||
def get_available_resource(self):
|
|
||||||
"""Retrieve resource info.
|
|
||||||
|
|
||||||
This method is called when nova-compute launches, and
|
|
||||||
as part of a periodic task.
|
|
||||||
|
|
||||||
:returns: dictionary describing resources
|
|
||||||
|
|
||||||
"""
|
|
||||||
LOG.info(_('get_available_resource called'))
|
|
||||||
|
|
||||||
# TODO(alexpilotti) implemented cpu_info
|
|
||||||
dic = {'vcpus': self._get_vcpu_total(),
|
|
||||||
'memory_mb': self._get_memory_mb_total(),
|
|
||||||
'local_gb': self._get_local_gb_total(),
|
|
||||||
'vcpus_used': self._get_vcpu_used(),
|
|
||||||
'memory_mb_used': self._get_memory_mb_used(),
|
|
||||||
'local_gb_used': self._get_local_gb_used(),
|
|
||||||
'hypervisor_type': "hyperv",
|
|
||||||
'hypervisor_version': self._get_hypervisor_version(),
|
|
||||||
'hypervisor_hostname': platform.node(),
|
|
||||||
'cpu_info': 'unknown'}
|
|
||||||
|
|
||||||
return dic
|
|
||||||
|
|
||||||
def _cache_image(self, fn, target, fname, cow=False, Size=None,
|
def _cache_image(self, fn, target, fname, cow=False, Size=None,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
"""Wrapper for a method that creates an image that caches the image.
|
"""Wrapper for a method that creates an image that caches the image.
|
||||||
|
Reference in New Issue
Block a user