Add support for reporting host resources

Add initial setup code to connect to an HMC (until NEO is
available). Add code for reporting the host resources.
Remove get_host_stats() since it was removed in OpenStack.

Change-Id: I6d905eccf71be86cfb97c7fd30e0b8049e4ea1b6
This commit is contained in:
Kyle L. Henderson 2014-12-04 13:10:07 -06:00
parent b085b68000
commit d7b44936c5
4 changed files with 241 additions and 11 deletions

View File

@ -14,19 +14,48 @@
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Drew Thorstensen, IBM Corp.
import logging
import mock
from nova import test
from nova.virt import fake
from pypowervm.tests.wrappers.util import pvmhttp
from pypowervm.wrappers import constants as wpr_consts
import pypowervm.wrappers.managed_system as msentry_wrapper
from nova_powervm.virt.powervm import driver
from nova_powervm.virt.powervm import host as pvm_host
MS_HTTPRESP_FILE = "managedsystem.txt"
MC_HTTPRESP_FILE = "managementconsole.txt"
MS_NAME = 'HV4'
LOG = logging.getLogger(__name__)
logging.basicConfig()
class TestPowerVMDriver(test.TestCase):
def setUp(self):
super(TestPowerVMDriver, self).setUp()
ms_http = pvmhttp.load_pvm_resp(MS_HTTPRESP_FILE)
self.assertNotEqual(ms_http, None,
"Could not load %s " %
MS_HTTPRESP_FILE)
entries = ms_http.response.feed.findentries(
wpr_consts.SYSTEM_NAME, MS_NAME)
self.assertNotEqual(entries, None,
"Could not find %s in %s" %
(MS_NAME, MS_HTTPRESP_FILE))
self.myentry = entries[0]
self.wrapper = msentry_wrapper.ManagedSystem(self.myentry)
def test_driver_create(self):
"""Validates that a driver of the PowerVM type can just be
initialized.
@ -34,9 +63,39 @@ class TestPowerVMDriver(test.TestCase):
test_drv = driver.PowerVMDriver(fake.FakeVirtAPI())
self.assertIsNotNone(test_drv)
def test_driver_init(self):
@mock.patch('pypowervm.adapter.Session')
@mock.patch('pypowervm.adapter.Adapter')
@mock.patch('nova_powervm.virt.powervm.host.find_entry_by_mtm_serial')
def test_driver_init(self, mock_find, mock_apt, mock_sess):
"""Validates the PowerVM driver can be initialized for the host."""
drv = driver.PowerVMDriver(fake.FakeVirtAPI())
drv.init_host('FakeHost')
# Nothing to really check here specific to the host.
self.assertIsNotNone(drv)
def test_host_resources(self):
stats = pvm_host.build_host_resource_from_entry(self.wrapper)
self.assertIsNotNone(stats)
# Check for the presence of fields
fields = (('vcpus', 500), ('vcpus_used', 0),
('memory_mb', 5242880), ('memory_mb_used', 128),
'local_gb', 'local_gb_used', 'hypervisor_type',
'hypervisor_version', 'hypervisor_hostname', 'cpu_info',
'supported_instances', 'stats')
for fld in fields:
if isinstance(fld, tuple):
value = stats.get(fld[0], None)
self.assertEqual(value, fld[1])
else:
value = stats.get(fld, None)
self.assertIsNotNone(value)
# Check for individual stats
hstats = (('proc_units', '500.00'), ('proc_units_used', '0.00'))
for stat in hstats:
if isinstance(stat, tuple):
value = stats['stats'].get(stat[0], None)
self.assertEqual(value, stat[1])
else:
value = stats['stats'].get(stat, None)
self.assertIsNotNone(value)

View File

@ -13,3 +13,26 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.config import cfg
hmc_opts = [
# TODO(kyleh) Temporary - Only needed since we're using an HMC
cfg.StrOpt('hmc_host_id',
default='',
help='TEMP - the unique id of the host to manage'),
cfg.StrOpt('hmc_ip',
default='',
help='TEMP - the HMC IP.'),
cfg.StrOpt('hmc_user',
default='',
help='TEMP - the HMC user.'),
cfg.StrOpt('hmc_pass',
default='',
help='TEMP - the HMC password.')
]
CONF = cfg.CONF
CONF.register_opts(hmc_opts)
CONF.import_opt('host', 'nova.netconf')

View File

@ -15,11 +15,22 @@
# under the License.
from nova.i18n import _LI
from nova.openstack.common import log as logging
from nova.virt import driver
from nova.virt import fake # TODO(IBM): Remove this in the future
from oslo.config import cfg
from pypowervm import adapter as pvm_apt
from pypowervm.helpers import log_helper as log_hlp
from pypowervm.wrappers import constants as pvm_consts
from pypowervm.wrappers import managed_system as msentry_wrapper
from nova_powervm.virt.powervm import host as pvm_host
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class PowerVMDriver(driver.ComputeDriver):
@ -30,14 +41,39 @@ class PowerVMDriver(driver.ComputeDriver):
super(PowerVMDriver, self).__init__(virtapi)
# Use the fake driver for scaffolding for now
fake.set_nodes(['fake-PowerVM'])
# fake.set_nodes(['fake-PowerVM'])
self._fake = fake.FakeDriver(virtapi)
def init_host(self, host):
"""Initialize anything that is necessary for the driver to function,
including catching up with currently running VM's on the given host.
"""
pass
# Get an adapter
self._get_adapter()
# First need to resolve the managed host UUID
self._get_host_uuid()
LOG.info(_LI("The compute driver has been initialized."))
def _get_adapter(self):
# Decode the password
password = CONF.hmc_pass.decode('base64', 'strict')
# TODO(IBM): set cert path
self.session = pvm_apt.Session(CONF.hmc_ip, CONF.hmc_user, password,
certpath=None)
self.adapter = pvm_apt.Adapter(self.session,
helpers=log_hlp.log_helper)
def _get_host_uuid(self):
# Need to get a list of the hosts, then find the matching one
resp = self.adapter.read(pvm_consts.MGT_SYS)
host_entry = pvm_host.find_entry_by_mtm_serial(resp, CONF.hmc_host_id)
if not host_entry:
raise Exception("Host %s not found" % CONF.hmc_host_id)
self.host_wrapper = msentry_wrapper.ManagedSystem(host_entry)
self.host_uuid = self.host_wrapper.get_uuid()
LOG.info(_LI("Host UUID is:%s") % self.host_uuid)
def get_info(self, instance):
"""Get the current status of an instance, by name (not ID!)
@ -149,7 +185,10 @@ class PowerVMDriver(driver.ComputeDriver):
:returns: Dictionary describing resources
"""
data = self._fake.get_available_resource(nodename)
resp = self.adapter.read(pvm_consts.MGT_SYS, rootId=self.host_uuid)
if resp:
self.host_wrapper = msentry_wrapper.ManagedSystem(resp.entry)
data = pvm_host.build_host_resource_from_entry(self.host_wrapper)
return data
def get_host_uptime(self, host):
@ -164,12 +203,6 @@ class PowerVMDriver(driver.ComputeDriver):
"""Unplug VIFs from networks."""
pass
def get_host_stats(self, refresh=False):
"""Return currently known host stats."""
data = self._fake.get_host_stats(refresh)
return data
def get_available_nodes(self):
"""Returns nodenames of all nodes managed by the compute service.

View File

@ -0,0 +1,115 @@
# Copyright 2014 IBM Corp.
#
# 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.
import math
from nova.compute import arch
from nova.compute import hv_type
from nova.compute import vm_mode
from nova.openstack.common import log as logging
from oslo.serialization import jsonutils
from pypowervm.wrappers import constants as pvm_consts
from pypowervm.wrappers import managed_system as msentry_wrapper
LOG = logging.getLogger(__name__)
# Power VM hypervisor info
IBM_POWERVM_HYPERVISOR_VERSION = '7.1'
# The types of LPARS that are supported.
POWERVM_SUPPORTED_INSTANCES = jsonutils.dumps([(arch.PPC64,
hv_type.PHYP,
vm_mode.HVM),
(arch.PPC64LE,
hv_type.PHYP,
vm_mode.HVM)])
# cpu_info that will be returned by build_host_stats_from_entry()
HOST_STATS_CPU_INFO = jsonutils.dumps({'vendor': 'ibm', 'arch': 'ppc64'})
def parse_mtm(mtm_serial):
mtm, serial = mtm_serial.split('_', 1)
mt = mtm[0:4]
md = mtm[4:7]
return mt, md, serial
def get_mtm_serial(msentry):
mt = msentry.get_type()
md = msentry.get_model()
ms = msentry.get_serial()
return mt + md + '_' + ms
def find_entry_by_mtm_serial(resp, mtm_serial):
mt, md, serial = parse_mtm(mtm_serial)
entries = resp.feed.findentries(pvm_consts.MACHINE_SERIAL, serial)
if entries is None:
return None
else:
LOG.info("Entry %s" % entries)
# Confirm same model and type
for entry in entries:
wrapper = msentry_wrapper.ManagedSystem(entry)
if (wrapper.get_type() == mt and wrapper.get_model() == md):
return entry
# No matching MTM Serial was found
return None
def build_host_resource_from_entry(msentry):
"""Build the host resource dict from an MS adapter wrapper
This method builds the host resource dictionary from the
ManagedSystem Entry wrapper
:param msentry: ManagedSystem Entry Wrapper.
"""
data = {}
# Calculate the vcpus
proc_units = msentry.get_proc_units_configurable()
proc_units_avail = msentry.get_proc_units_avail()
pu_used = float(proc_units) - float(proc_units_avail)
data['vcpus'] = int(math.ceil(float(proc_units)))
data['vcpus_used'] = int(math.ceil(pu_used))
data['memory_mb'] = msentry.get_memory_configurable()
data['memory_mb_used'] = (msentry.get_memory_configurable() -
msentry.get_memory_free())
# TODO(IBM): make the local gb large for now
data["local_gb"] = (1 << 21)
data["local_gb_used"] = 0
data["hypervisor_type"] = hv_type.PHYP
data["hypervisor_version"] = IBM_POWERVM_HYPERVISOR_VERSION
# TODO(IBM): Get the right host name
data["hypervisor_hostname"] = get_mtm_serial(msentry)
data["cpu_info"] = HOST_STATS_CPU_INFO
data["numa_topology"] = None
data["supported_instances"] = POWERVM_SUPPORTED_INSTANCES
stats = {'proc_units': '%.2f' % float(proc_units),
'proc_units_used': '%.2f' % pu_used
}
data["stats"] = stats
return data