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:
parent
b085b68000
commit
d7b44936c5
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue