Updating flavor creation and management to support usage of generic RSD flavors

- Implemented new functionality
 - Updated existing unit tests

Change-Id: Ib7449bec27db7f2636485b0ee7658fd2a5e471e5
Signed-off-by: Helena McGough <helena.mcgough@intel.com>
This commit is contained in:
Helena McGough 2019-05-27 15:57:56 +00:00 committed by Helena
parent 64bb9f07a9
commit c4eab35f35
3 changed files with 141 additions and 158 deletions

View File

@ -340,16 +340,17 @@ class TestRSDDriver(base.BaseTestCase):
mock_context = context.get_admin_context()
self.RSD.rsd_flavors = {
self.flavor.flavorid: {
'rsd_systems': {
'/redfish/v1/Chassis/Chassis1':
self.system_inst.path
'rsd_systems': self.system_inst.path
}
}
}
self.RSD.system_rp_id = {'mock_rp_id': '/redfish/v1/Systems/System1'}
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
# Run spawning test
self.RSD.spawn(mock_context, self.inst1, image_meta,
[], None, {})
[], None, {'mock_rp_id': {
'generation': 1,
'resources': {'VCPUS': 1,
'MEM_MB': 4126}}})
# Confirm that a node instances is spawned and the physical composed
# node is powered on
@ -359,7 +360,7 @@ class TestRSDDriver(base.BaseTestCase):
self.assertEquals(node.composed_node_state.lower(), 'assembled')
self.assertEquals(node.power_state.lower(), 'off')
self.assertEquals(self.inst1.display_description,
json.dumps({"node_identity": "/redfish/v1/Nodes/Node1",
json.dumps({"node_identity": node.name,
"node_uuid": node.uuid}))
power_on.assert_called_once_with(mock_context, self.inst1, None)
@ -510,8 +511,9 @@ class TestRSDDriver(base.BaseTestCase):
self.RSD.driver.PODM.get_chassis_collection.assert_called()
chas_col.get_member.assert_called_with('/redfish/v1/Chassis/Chassis1')
check_chas.assert_called_with(self.chassis_inst)
create_child_inv.assert_called_once_with('/redfish/v1/Systems/System1')
create_inv.assert_called_once_with(check_chas.return_value)
create_child_inv.assert_called_once_with(
['/redfish/v1/Systems/System1'])
create_inv.assert_called_once_with(1)
@mock.patch.object(driver.RSDDriver, 'create_child_inventory')
@mock.patch.object(driver.RSDDriver, 'create_inventory')
@ -692,7 +694,7 @@ class TestRSDDriver(base.BaseTestCase):
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
@mock.patch.object(driver.RSDDriver, 'get_sys_memory_info')
@mock.patch.object(driver.RSDDriver, 'get_sys_proc_info')
def test_create_inventory(self, sys_proc_info, sys_mem_info,
def test_create_child_inventory(self, sys_proc_info, sys_mem_info,
conv_mem):
"""Test creating a inventory for a provider tree."""
# Setup test to successfully create inventory
@ -700,7 +702,7 @@ class TestRSDDriver(base.BaseTestCase):
self.system_inst.memory_summary.total_system_memory_gib
sys_proc_info.return_value = \
self.system_inst.json['ProcessorSummary']['Count']
inv = self.RSD.create_inventory([self.system_inst.identity])
inv = self.RSD.create_child_inventory([self.system_inst.identity])
# Check that the correct functions are called and the inventory
# is generated correctly
@ -723,27 +725,15 @@ class TestRSDDriver(base.BaseTestCase):
'allocation_ratio': 1}
})
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
@mock.patch.object(fields.ResourceClass, 'normalize_name')
def test_create_child_inventory(self, norm_name, conv_mem):
def test_create_inventory(self, norm_name):
"""Test creating inventory for the child RP's."""
# Set up a test to create the inventory for child resource providers
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
sys_col.get_member.return_value = self.system_inst
mem = conv_mem.return_value - 512
proc = self.system_inst.json['ProcessorSummary']['Count']
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
child_inv = self.RSD.create_child_inventory(
'/redfish/v1/Systems/System1')
child_inv = self.RSD.create_inventory(1)
# Check that the correct functions are called and the inventory
# is generated correctly
self.RSD.driver.PODM.get_system_collection.assert_called_once()
sys_col.get_member.assert_called_once_with(
'/redfish/v1/Systems/System1')
conv_mem.assert_called_once_with(
self.system_inst.memory_summary.total_system_memory_gib)
norm_name.assert_called_once_with(flav_id)
norm_name.assert_called_once_with('RSD')
self.assertEqual(child_inv, {norm_name.return_value: {
'total': 1,
'reserved': 0,
@ -776,66 +766,63 @@ class TestRSDDriver(base.BaseTestCase):
@mock.patch.object(flavor_management.FlavorManager, '_create_request_url')
@mock.patch.object(driver, 'requests')
@mock.patch.object(fields.ResourceClass, 'normalize_name')
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
def test_create_flavors(self, check_chas, norm_name, reqs, create_url):
def test_create_flavors(self, norm_name, reqs, create_url):
"""Test successfully creating a new flavor."""
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
sys_col.get_member.return_value = self.system_inst
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
chas_col.members_identities = ['/redfish/v1/Chassis/Chassis1']
chas_col.get_member.return_value = self.chassis_inst
check_chas.return_value = ['/redfish/v1/Systems/System1']
sys_col.members_identities = ['/redfish/v1/Systems/System1']
self.RSD._url_base = "mock_url_base"
self.RSD.headers = "headers"
mem = self.RSD.conv_GiB_to_MiB(
self.system_inst.memory_summary.total_system_memory_gib) - 512
proc = self.system_inst.json['ProcessorSummary']['Count']
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
spec = str('resources:' + norm_name.return_value)
mock_specs = {
'extra_specs': {
spec: '1'}}
response = {"flavors": [
{"id": "1",
"links": [
{"href": "http://openstack.example.com/v2/6f70656e/flavors/1",
"rel": "self"
},
{"href": "http://openstack.example.com/6f70656e/flavors/1",
"rel": "bookmark"
}],
"name": "m1.tiny"
},
{"id": "2",
"links": [
{"href": "http://openstack.example.com/v2/6f70656e/flavors/2",
"rel": "self"
},
{"href": "http://openstack.example.com/6f70656e/flavors/2",
"rel": "bookmark"
}],
"name": "m1.small"
}]
}
resp = FakeResponse(response, 201)
reqs.get.return_value.text = json.dumps(resp.text)
# Run Test
self.RSD._create_flavors()
self.RSD.driver.PODM.get_system_collection.assert_called_once()
self.RSD.driver.PODM.get_chassis_collection.assert_called_once()
chas_col.get_member.assert_called_with(self.chassis_inst.path)
check_chas.assert_called_with(self.chassis_inst)
sys_col.get_member.assert_called_with(self.system_inst.path)
norm_name.assert_called_once_with(flav_id)
create_url.assert_called_once_with(flav_id, 'update')
norm_name.assert_called_once_with('RSD')
create_url.assert_called()
reqs.post.assert_called_with(create_url.return_value,
data=json.dumps(mock_specs), headers="headers")
self.assertEquals(self.RSD.chas_systems,
{str(self.chassis_inst.path):
[str(self.system_inst.path)]})
self.assertEquals(self.RSD.rsd_flavors,
{flav_id: {
'rsd_systems': self.RSD.chas_systems}})
@mock.patch.object(requests, 'post')
@mock.patch.object(fields.ResourceClass, 'normalize_name')
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
def test_create_flavors_exists(self, check_chas, norm_name, post_req):
def test_create_flavors_failure(self, norm_name, post_req):
"""Test existing failure to create a new flavor."""
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
sys_col.get_member.return_value = self.system_inst
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
chas_col.members_identities = ['/redfish/v1/Chassis/Chassis1']
chas_col.get_member.return_value = self.chassis_inst
check_chas.return_value = ['/redfish/v1/Systems/System1']
# Run Test
self.RSD._create_flavors()
self.RSD.driver.PODM.get_system_collection.assert_called_once()
self.RSD.driver.PODM.get_chassis_collection.assert_called_once()
chas_col.get_member.assert_called_with(self.chassis_inst.path)
check_chas.assert_called_with(self.chassis_inst)
sys_col.get_member.assert_called_with(self.system_inst.path)
sys_col.get_member.assert_not_called()
post_req.assert_not_called()
@mock.patch.object(flavor_management.FlavorManager, '_create_request_url')

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# 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
@ -71,7 +70,7 @@ class RSDDriver(driver.ComputeDriver):
self.driver = rsd.PODM_connection()
self.instances = OrderedDict()
self.rsd_flavors = OrderedDict()
self.chas_systems = OrderedDict()
self.system_rp_id = OrderedDict()
self._nodes = []
self._composed_nodes = OrderedDict()
self.instance_node = None
@ -127,30 +126,39 @@ class RSDDriver(driver.ComputeDriver):
# Assembles the composed node and tracks node from the instance
COMPOSED_NODE_COL = self.driver.PODM.get_node_collection()
node_inst = None
rp_id = ''
nodes = {}
flav_id = instance.flavor.flavorid
sys_list = self.rsd_flavors[flav_id]['rsd_systems'][instance.node]
for n in COMPOSED_NODE_COL.members_identities:
try:
node_inst = self.driver.PODM.get_node(n)
except Exception as ex:
LOG.warn("Malformed composed node instance:%s", ex)
sys_list = self.rsd_flavors[flav_id]['rsd_systems']
for r in allocations.keys():
values = allocations[r]
for v in values.values():
if isinstance(v, int) is False:
if 'CUSTOM_RSD' not in v:
rp_id = r
sys_id = self.system_rp_id[rp_id]
if sys_id in sys_list:
for n in COMPOSED_NODE_COL.members_identities:
try:
node_inst = self.driver.PODM.get_node(n)
nodes[node_inst.links.computer_system] = node_inst
except Exception as ex:
LOG.warn("Malformed composed node instance:%s", ex)
if node_inst is not None:
node_state = node_inst.composed_node_state.lower()
p_state = node_inst.power_state.lower()
if sys_id in nodes.keys():
# Provide nova instance with composed node info
node = nodes[sys_id]
node_state = node.composed_node_state.lower()
if node_state == 'assembled':
# Provide nova instance with composed node info
node_sys_id = node_inst.links.computer_system
if node_sys_id in sys_list:
self.instances[uuid] = instance
self._composed_nodes[uuid] = node_inst
instance.display_description = \
json.dumps({"node_identity": n,
"node_uuid": node_inst.uuid})
if p_state == 'off':
self.power_on(context, instance, network_info)
return
self.instances[uuid] = instance
self._composed_nodes[uuid] = node
instance.display_description = \
json.dumps({"node_identity": node.name,
"node_uuid": node.uuid})
if node.power_state.lower() == 'off':
self.power_on(context, instance, network_info)
LOG.info("Booted composed node:%s", n)
return
raise Exception("Failed to assign composed node for instance.")
def destroy(self, context, instance, network_info, block_device_info=None,
@ -257,10 +265,12 @@ class RSDDriver(driver.ComputeDriver):
# Removing all RPS that don't have an associated system
if s_tree.name not in systems:
provider_tree.remove(str(s_tree.uuid))
elif s_tree.uuid not in self.system_rp_id.keys():
self.system_rp_id[s_tree.uuid] = s_tree.name
chassis = CHASSIS_COL.get_member(chas_tree.name)
if self.check_chassis_systems(chassis) == []:
provider_tree.remove(str(chas_tree.uuid))
chassis = CHASSIS_COL.get_member(chas_tree.name)
if self.check_chassis_systems(chassis) == []:
provider_tree.remove(str(chas_tree.uuid))
self._nodes = self._init_nodes()
for c in CHASSIS_COL.members_identities:
@ -272,13 +282,14 @@ class RSDDriver(driver.ComputeDriver):
if cha_sys != []:
for s in cha_sys:
sys_inv = self.create_child_inventory(s)
sys_inv = self.create_child_inventory([s])
try:
provider_tree.new_child(s, nodename)
prov_id = provider_tree.new_child(s, nodename)
self.system_rp_id[prov_id] = s
except Exception as ex:
LOG.warn("Failed to create new RP: %s", ex)
provider_tree.update_inventory(s, sys_inv)
chas_inv = self.create_inventory(cha_sys)
chas_inv = self.create_inventory(len(cha_sys))
provider_tree.update_inventory(nodename, chas_inv)
def get_sys_proc_info(self, systems):
@ -372,7 +383,7 @@ class RSDDriver(driver.ComputeDriver):
LOG.info("Graceful restart composed node for reboot: %s", key)
node_inst.reset_node('GracefulRestart')
def create_inventory(self, system):
def create_child_inventory(self, system):
"""Function to create provider tree inventory."""
mem_max = 1
proc_max = 1
@ -400,18 +411,12 @@ class RSDDriver(driver.ComputeDriver):
}
}
def create_child_inventory(self, system):
def create_inventory(self, no_systems):
"""Create custom resources for all of the child RP's."""
SYSTEM_COL = self.driver.PODM.get_system_collection()
sys = SYSTEM_COL.get_member(system)
mem = self.conv_GiB_to_MiB(
sys.memory_summary.total_system_memory_gib) - 512
proc = sys.json['ProcessorSummary']['Count']
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
res = fields.ResourceClass.normalize_name(flav_id)
res = fields.ResourceClass.normalize_name('RSD')
return {
res: {
'total': 1,
'total': no_systems,
'reserved': 0,
'min_unit': 1,
'max_unit': 1,
@ -435,68 +440,57 @@ class RSDDriver(driver.ComputeDriver):
def _create_flavors(self):
"""Auto-generate the flavors for the compute systems available."""
SYSTEM_COL = self.driver.PODM.get_system_collection()
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
for c in CHASSIS_COL.members_identities:
chas = CHASSIS_COL.get_member(c)
cha_sys = self.check_chassis_systems(chas)
for s in cha_sys:
sys = SYSTEM_COL.get_member(s)
mem = self.conv_GiB_to_MiB(
sys.memory_summary.total_system_memory_gib) - 512
proc = sys.json['ProcessorSummary']['Count']
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
res = fields.ResourceClass.normalize_name(flav_id)
spec = str('resources:' + res)
payload = {
"flavor": {
'name': 'RSD-' + flav_id,
'id': flav_id,
'ram': mem,
'vcpus': proc,
'disk': 0
for s in SYSTEM_COL.members_identities:
sys = SYSTEM_COL.get_member(s)
mem = self.conv_GiB_to_MiB(
sys.memory_summary.total_system_memory_gib) - 512
proc = sys.json['ProcessorSummary']['Count']
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
res = fields.ResourceClass.normalize_name('RSD')
spec = str('resources:' + res)
payload = {
"flavor": {
'name': 'RSD-' + flav_id,
'id': flav_id,
'ram': mem,
'vcpus': proc,
'disk': 0
}
}
try:
requests.post(
self._url_base, data=json.dumps(payload),
headers=self.headers)
except Exception as ex:
LOG.warn("Failed to create the new flavor: %s", ex)
try:
extra_pay = {
'extra_specs': {
spec: '1'}
}
}
if flav_id not in self.rsd_flavors.keys():
try:
requests.post(
self._url_base, data=json.dumps(payload),
headers=self.headers)
except Exception as ex:
LOG.warn("Failed to create the new flavor: %s", ex)
try:
extra_pay = {
'extra_specs': {
spec: '1'}
}
update_url = self.flavor_manager._create_request_url(
flav_id, 'update')
requests.post(
update_url, data=json.dumps(extra_pay),
headers=self.headers)
self.chas_systems[str(chas.path)] = [
str(sys.path)]
self.rsd_flavors[flav_id] = {
'rsd_systems': self.chas_systems}
except Exception as ex:
LOG.warn("Failed to add extra_specs:%s", ex)
else:
chassis_ = self.rsd_flavors[flav_id]['rsd_systems']
if str(chas.path) not in chassis_.keys():
self.chas_systems[str(chas.path)] = [
str(sys.path)]
self.rsd_flavors[flav_id] = {
'rsd_systems': self.chas_systems
}
else:
systems = self.rsd_flavors[
flav_id]['rsd_systems'][str(chas.path)]
if str(sys.path) not in systems:
systems.append(str(sys.path))
self.chas_systems[str(chas.path)] = systems
self.rsd_flavors[flav_id] = {
'rsd_systems': self.chas_systems
}
update_url = self.flavor_manager._create_request_url(
flav_id, 'update')
requests.post(
update_url, data=json.dumps(extra_pay),
headers=self.headers)
except Exception as ex:
LOG.warn("Failed to add extra_specs:%s", ex)
get_url = self.flavor_manager._create_request_url(None, 'get')
req = requests.get(get_url, headers=self.headers)
f_list = json.loads(req.text)['flavors']
for f in f_list:
if 'RSD' in f['name'] and \
mem >= f['ram'] and proc >= f['vcpus']:
if f['id'] not in self.rsd_flavors:
self.rsd_flavors[f['id']] = {
'rsd_systems': []}
systems = self.rsd_flavors[f['id']]['rsd_systems']
if s not in systems:
systems.append(s)
self.rsd_flavors[f['id']] = {
'rsd_systems': systems}
def check_flavors(self, collection, systems):
"""Check if flavors should be deleted based on system removal."""
@ -529,7 +523,7 @@ class RSDDriver(driver.ComputeDriver):
for k in self.rsd_flavors.keys():
rsd_id = self.rsd_flavors[k]
sys_list = self.rsd_flavors[k]['rsd_systems'].values()
sys_list = self.rsd_flavors[k]['rsd_systems']
for s in sys_list:
if s not in sys_ids:
del_url = self.flavor_manager._create_request_url(

View File

@ -80,6 +80,8 @@ class FlavorManager(object):
url = "{}/flavors/%s".format(endpoint) % (flavorid)
elif req_type == 'update':
url = "{}/flavors/%s/os-extra_specs".format(endpoint) % (flavorid)
elif req_type == 'get':
url = "{}/flavors/detail".format(endpoint)
return url
def get_headers(self, auth_token):