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
|
||||
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()
|
||||
|
@@ -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
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.
|
||||
"""
|
||||
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.
|
||||
|
Reference in New Issue
Block a user