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:
Alessandro Pilotti
2012-10-31 19:38:55 +02:00
parent 3157a42744
commit 80fb666fa2
6 changed files with 184 additions and 112 deletions

View File

@@ -103,6 +103,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
# Modules in which the mocks are going to be injected
from nova.virt.hyperv import baseops
from nova.virt.hyperv import hostops
from nova.virt.hyperv import livemigrationops
from nova.virt.hyperv import snapshotops
from nova.virt.hyperv import vmops
@@ -112,6 +113,7 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
modules_to_test = [
driver_hyperv,
baseops,
hostops,
vmops,
vmutils,
volumeops,
@@ -157,6 +159,14 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
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):
num_vms = self._hypervutils.get_vm_count()
instances = self._conn.list_instances()

View File

@@ -63,6 +63,7 @@ Using the Python WMI library:
from nova.openstack.common import log as logging
from nova.virt import driver
from nova.virt.hyperv import hostops
from nova.virt.hyperv import livemigrationops
from nova.virt.hyperv import snapshotops
from nova.virt.hyperv import vmops
@@ -75,6 +76,7 @@ class HyperVDriver(driver.ComputeDriver):
def __init__(self):
super(HyperVDriver, self).__init__()
self._hostops = hostops.HostOps()
self._volumeops = volumeops.VolumeOps()
self._vmops = vmops.VMOps(self._volumeops)
self._snapshotops = snapshotops.SnapshotOps()
@@ -121,15 +123,13 @@ class HyperVDriver(driver.ComputeDriver):
pass
def get_available_resource(self):
return self._vmops.get_available_resource()
return self._hostops.get_available_resource()
def get_host_stats(self, refresh=False):
"""See xenapi_conn.py implementation."""
return {}
return self._hostops.get_host_stats(refresh)
def host_power_action(self, host, action):
"""Reboots, shuts down or powers up the host."""
pass
return self._hostops.host_power_action(host, action)
def set_host_enabled(self, host, enabled):
"""Sets the specified host's ability to accept new instances."""

169
nova/virt/hyperv/hostops.py Normal file
View 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

View File

@@ -18,9 +18,7 @@
"""
Management class for basic VM operations.
"""
import multiprocessing
import os
import platform
import uuid
from nova import db
@@ -475,111 +473,6 @@ class VMOps(baseops.BaseOps):
LOG.error(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,
*args, **kwargs):
"""Wrapper for a method that creates an image that caches the image.