Skip to content

Commit 581427d

Browse files
author
David Ames
authored
Merge pull request openstack-charmers#437 from gabriel-samfira/add-ironic-tests
Add Ironic tests
2 parents 41c5434 + 7c2f5cd commit 581427d

File tree

7 files changed

+329
-13
lines changed

7 files changed

+329
-13
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ python-ceilometerclient
2929
python-cinderclient
3030
python-glanceclient
3131
python-heatclient
32+
python-ironicclient
3233
python-keystoneclient
3334
python-manilaclient
3435
python-neutronclient

unit_tests/utilities/test_zaza_utilities_openstack.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ def test_upload_image_to_glance(self):
504504
glance_mock.images.upload.assert_called_once_with(
505505
'9d1125af',
506506
f(),
507-
)
507+
backend=None)
508508
self.resource_reaches_status.assert_called_once_with(
509509
glance_mock.images,
510510
'9d1125af',
@@ -529,7 +529,11 @@ def test_create_image_use_tempdir(self):
529529
self.upload_image_to_glance.assert_called_once_with(
530530
glance_mock,
531531
'wibbly/c.img',
532-
'bob')
532+
'bob',
533+
backend=None,
534+
disk_format='qcow2',
535+
visibility='public',
536+
container_format='bare')
533537

534538
def test_create_image_pass_directory(self):
535539
glance_mock = mock.MagicMock()
@@ -549,7 +553,11 @@ def test_create_image_pass_directory(self):
549553
self.upload_image_to_glance.assert_called_once_with(
550554
glance_mock,
551555
'tests/c.img',
552-
'bob')
556+
'bob',
557+
backend=None,
558+
disk_format='qcow2',
559+
visibility='public',
560+
container_format='bare')
553561
self.gettempdir.assert_not_called()
554562

555563
def test_create_ssh_key(self):

zaza/openstack/charm_tests/glance/setup.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,37 @@ def basic_setup():
3232
"""
3333

3434

35+
def _get_default_glance_client():
36+
"""Create default Glance client using overcloud credentials."""
37+
keystone_session = openstack_utils.get_overcloud_keystone_session()
38+
glance_client = openstack_utils.get_glance_session_client(keystone_session)
39+
return glance_client
40+
41+
42+
def get_stores_info(glance_client=None):
43+
"""Retrieve glance backing store info.
44+
45+
:param glance_client: Authenticated glanceclient
46+
:type glance_client: glanceclient.Client
47+
"""
48+
glance_client = glance_client or _get_default_glance_client()
49+
stores = glance_client.images.get_stores_info().get("stores", [])
50+
return stores
51+
52+
53+
def get_store_ids(glance_client=None):
54+
"""Retrieve glance backing store ids.
55+
56+
:param glance_client: Authenticated glanceclient
57+
:type glance_client: glanceclient.Client
58+
"""
59+
stores = get_stores_info(glance_client)
60+
return [store["id"] for store in stores]
61+
62+
3563
def add_image(image_url, glance_client=None, image_name=None, tags=[],
36-
properties=None):
64+
properties=None, backend=None, disk_format='qcow2',
65+
visibility='public', container_format='bare'):
3766
"""Retrieve image from ``image_url`` and add it to glance.
3867
3968
:param image_url: Retrievable URL with image data
@@ -47,10 +76,14 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[],
4776
:param properties: Properties to add to image
4877
:type properties: dict
4978
"""
50-
if not glance_client:
51-
keystone_session = openstack_utils.get_overcloud_keystone_session()
52-
glance_client = openstack_utils.get_glance_session_client(
53-
keystone_session)
79+
glance_client = glance_client or _get_default_glance_client()
80+
if backend is not None:
81+
stores = get_store_ids(glance_client)
82+
if backend not in stores:
83+
raise ValueError("Invalid backend: %(backend)s "
84+
"(available: %(available)s)" % {
85+
"backend": backend,
86+
"available": ", ".join(stores)})
5487
if image_name:
5588
image = openstack_utils.get_images_by_name(
5689
glance_client, image_name)
@@ -65,7 +98,11 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[],
6598
image_url,
6699
image_name,
67100
tags=tags,
68-
properties=properties)
101+
properties=properties,
102+
backend=backend,
103+
disk_format=disk_format,
104+
visibility=visibility,
105+
container_format=container_format)
69106

70107

71108
def add_cirros_image(glance_client=None, image_name=None):
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2020 Canonical Ltd.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Collection of code for setting up and testing ironic."""
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Copyright 2020 Canonical Ltd.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Code for configuring ironic."""
16+
17+
import copy
18+
import os
19+
20+
import zaza.openstack.charm_tests.glance.setup as glance_setup
21+
import zaza.openstack.utilities.openstack as openstack_utils
22+
from zaza.openstack.utilities import (
23+
cli as cli_utils,
24+
)
25+
import zaza.model as zaza_model
26+
27+
28+
FLAVORS = {
29+
'bm1.small': {
30+
'flavorid': 2,
31+
'ram': 2048,
32+
'disk': 20,
33+
'vcpus': 1,
34+
'properties': {
35+
"resources:CUSTOM_BAREMETAL1_SMALL": 1,
36+
},
37+
},
38+
'bm1.medium': {
39+
'flavorid': 3,
40+
'ram': 4096,
41+
'disk': 40,
42+
'vcpus': 2,
43+
'properties': {
44+
"resources:CUSTOM_BAREMETAL1_MEDIUM": 1,
45+
},
46+
},
47+
'bm1.large': {
48+
'flavorid': 4,
49+
'ram': 8192,
50+
'disk': 40,
51+
'vcpus': 4,
52+
'properties': {
53+
"resources:CUSTOM_BAREMETAL1_LARGE": 1,
54+
},
55+
},
56+
'bm1.tempest': {
57+
'flavorid': 6,
58+
'ram': 256,
59+
'disk': 1,
60+
'vcpus': 1,
61+
'properties': {
62+
"resources:CUSTOM_BAREMETAL1_TEMPEST": 1,
63+
},
64+
},
65+
'bm2.tempest': {
66+
'flavorid': 7,
67+
'ram': 512,
68+
'disk': 1,
69+
'vcpus': 1,
70+
'properties': {
71+
"resources:CUSTOM_BAREMETAL2_TEMPEST": 1,
72+
},
73+
},
74+
}
75+
76+
77+
def add_ironic_deployment_image(initrd_url=None, kernel_url=None):
78+
"""Add Ironic deploy images to glance.
79+
80+
:param initrd_url: URL where the ari image resides
81+
:type initrd_url: str
82+
:param kernel_url: URL where the aki image resides
83+
:type kernel_url: str
84+
"""
85+
base_name = 'ironic-deploy'
86+
initrd_name = "{}-initrd".format(base_name)
87+
vmlinuz_name = "{}-vmlinuz".format(base_name)
88+
if not initrd_url:
89+
initrd_url = os.environ.get('TEST_IRONIC_DEPLOY_INITRD', None)
90+
if not kernel_url:
91+
kernel_url = os.environ.get('TEST_IRONIC_DEPLOY_VMLINUZ', None)
92+
if not all([initrd_url, kernel_url]):
93+
raise ValueError("Missing required deployment image URLs")
94+
glance_setup.add_image(
95+
initrd_url,
96+
image_name=initrd_name,
97+
backend="swift",
98+
disk_format="ari",
99+
container_format="ari")
100+
glance_setup.add_image(
101+
kernel_url,
102+
image_name=vmlinuz_name,
103+
backend="swift",
104+
disk_format="aki",
105+
container_format="aki")
106+
107+
108+
def add_ironic_os_image(image_url=None):
109+
"""Upload the operating system images built for bare metal deployments.
110+
111+
:param image_url: URL where the image resides
112+
:type image_url: str
113+
"""
114+
image_url = image_url or os.environ.get(
115+
'TEST_IRONIC_RAW_BM_IMAGE', None)
116+
image_name = "baremetal-ubuntu-image"
117+
if image_url is None:
118+
raise ValueError("Missing image_url")
119+
120+
glance_setup.add_image(
121+
image_url,
122+
image_name=image_name,
123+
backend="swift",
124+
disk_format="raw",
125+
container_format="bare")
126+
127+
128+
def set_temp_url_secret():
129+
"""Run the set-temp-url-secret on the ironic-conductor leader.
130+
131+
This is needed if direct boot method is enabled.
132+
"""
133+
zaza_model.run_action_on_leader(
134+
'ironic-conductor',
135+
'set-temp-url-secret',
136+
action_params={})
137+
138+
139+
def create_bm_flavors(nova_client=None):
140+
"""Create baremetal flavors.
141+
142+
:param nova_client: Authenticated nova client
143+
:type nova_client: novaclient.v2.client.Client
144+
"""
145+
if not nova_client:
146+
keystone_session = openstack_utils.get_overcloud_keystone_session()
147+
nova_client = openstack_utils.get_nova_session_client(
148+
keystone_session)
149+
cli_utils.setup_logging()
150+
names = [flavor.name for flavor in nova_client.flavors.list()]
151+
# Disable scheduling based on standard flavor properties
152+
default_properties = {
153+
"resources:VCPU": 0,
154+
"resources:MEMORY_MB": 0,
155+
"resources:DISK_GB": 0,
156+
}
157+
for flavor in FLAVORS.keys():
158+
if flavor not in names:
159+
properties = copy.deepcopy(default_properties)
160+
properties.update(FLAVORS[flavor]["properties"])
161+
bm_flavor = nova_client.flavors.create(
162+
name=flavor,
163+
ram=FLAVORS[flavor]['ram'],
164+
vcpus=FLAVORS[flavor]['vcpus'],
165+
disk=FLAVORS[flavor]['disk'],
166+
flavorid=FLAVORS[flavor]['flavorid'])
167+
bm_flavor.set_keys(properties)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright 2020 Canonical Ltd.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Encapsulate ironic testing."""
16+
17+
import logging
18+
19+
import ironicclient.client as ironic_client
20+
import zaza.openstack.charm_tests.test_utils as test_utils
21+
import zaza.openstack.utilities.openstack as openstack_utils
22+
23+
24+
def _get_ironic_client(ironic_api_version="1.58"):
25+
keystone_session = openstack_utils.get_overcloud_keystone_session()
26+
ironic = ironic_client.Client(1, session=keystone_session,
27+
os_ironic_api_version=ironic_api_version)
28+
return ironic
29+
30+
31+
class IronicTest(test_utils.OpenStackBaseTest):
32+
"""Run Ironic specific tests."""
33+
34+
_SERVICES = ['ironic-api']
35+
36+
def test_110_catalog_endpoints(self):
37+
"""Verify that the endpoints are present in the catalog."""
38+
overcloud_auth = openstack_utils.get_overcloud_auth()
39+
keystone_client = openstack_utils.get_keystone_client(
40+
overcloud_auth)
41+
actual_endpoints = keystone_client.service_catalog.get_endpoints()
42+
actual_interfaces = [endpoint['interface'] for endpoint in
43+
actual_endpoints["baremetal"]]
44+
for expected_interface in ('internal', 'admin', 'public'):
45+
assert(expected_interface in actual_interfaces)
46+
47+
def test_400_api_connection(self):
48+
"""Simple api calls to check service is up and responding."""
49+
ironic = _get_ironic_client()
50+
51+
logging.info('listing conductors')
52+
conductors = ironic.conductor.list()
53+
assert(len(conductors) > 0)
54+
55+
# By default, only IPMI HW type is enabled. iDrac and Redfish
56+
# can optionally be enabled
57+
drivers = ironic.driver.list()
58+
driver_names = [drv.name for drv in drivers]
59+
60+
expected = ['intel-ipmi', 'ipmi']
61+
for exp in expected:
62+
assert(exp in driver_names)
63+
assert(len(driver_names) == 2)
64+
65+
def test_900_restart_on_config_change(self):
66+
"""Checking restart happens on config change.
67+
68+
Change debug mode and assert that change propagates to the correct
69+
file and that services are restarted as a result
70+
"""
71+
self.restart_on_changed_debug_oslo_config_file(
72+
'/etc/ironic/ironic.conf', self._SERVICES)
73+
74+
def test_910_pause_resume(self):
75+
"""Run pause and resume tests.
76+
77+
Pause service and check services are stopped then resume and check
78+
they are started
79+
"""
80+
logging.info('Skipping pause resume test LP: #1886202...')
81+
return
82+
with self.pause_resume(self._SERVICES):
83+
logging.info("Testing pause resume")

0 commit comments

Comments
 (0)