Re-enable uefi support with glanceclient

Use glanceclient to examine image metadata instead of the nova
image proxy, which seems to have been removed in recent versions of
novaclient.
This commit is contained in:
Ben Nemec 2017-04-26 12:55:22 -05:00
parent 2fe2fd0569
commit 53320ee72b
7 changed files with 153 additions and 107 deletions

View File

@ -127,3 +127,32 @@ def _get_keystone_token():
project_domain_name=project_domain)
def _get_token_and_endpoint(name):
"""Return a token id and endpoint url for the specified service
:param name: The name of the service. heat, glance, etc.
:returns: A tuple of (token_id, service_endpoint)
"""
auth_data = _create_auth_parameters()
auth_url = auth_data['os_auth_url']
# Get token for service to use
if '/v3' not in auth_url:
token_data = _get_keystone_token()
token_id = token_data['token']['id']
catalog_key = 'serviceCatalog'
else:
token_data = _get_keystone_token()
token_id = token_data['auth_token']
catalog_key = 'catalog'
# Get service endpoint
for endpoint in token_data[catalog_key]:
if endpoint['name'] == name:
try:
# TODO: What if there's more than one endpoint?
service_endpoint = endpoint['endpoints'][0]['publicURL']
except KeyError:
# Keystone v3 endpoint data looks different
service_endpoint = [e for e in endpoint['endpoints']
if e['interface'] == 'public'][0]['url']
return token_id, service_endpoint

View File

@ -19,6 +19,7 @@ import os
import sys
import yaml
import glanceclient
from neutronclient.v2_0 import client as neutronclient
import novaclient as nc
from novaclient import client as novaclient
@ -91,6 +92,7 @@ def _get_clients():
import os_client_config
nova = os_client_config.make_client('compute', cloud=cloud)
neutron = os_client_config.make_client('network', cloud=cloud)
glance = os_client_config.make_client('image', cloud=cloud)
else:
auth_data = auth._create_auth_parameters()
@ -124,7 +126,10 @@ def _get_clients():
project_domain_name=project_domain)
sess = auth._get_keystone_session(auth_data)
neutron = neutronclient.Client(session=sess)
return nova, neutron
token, glance_endpoint = auth._get_token_and_endpoint('glance')
glance = glanceclient.Client('2', token=token,
endpoint=glance_endpoint)
return nova, neutron, glance
def _get_ports(neutron, bmc_base, baremetal_base):
@ -140,8 +145,8 @@ def _get_ports(neutron, bmc_base, baremetal_base):
return bmc_ports, bm_ports
def _build_nodes(nova, bmc_ports, bm_ports, provision_net, baremetal_base,
undercloud_name):
def _build_nodes(nova, glance, bmc_ports, bm_ports, provision_net,
baremetal_base, undercloud_name):
node_template = {
'pm_type': 'pxe_ipmitool',
'mac': '',
@ -177,12 +182,11 @@ def _build_nodes(nova, bmc_ports, bm_ports, provision_net, baremetal_base,
# If a node has uefi firmware ironic needs to be aware of this, in nova
# this is set using a image property called "hw_firmware_type"
# TODO(bnemec): Re-enable uefi support
#if not cache.get(baremetal.image['id']):
#cache[baremetal.image['id']] = nova.images.get(baremetal.image['id'])
#image = cache.get(baremetal.image['id'])
#if image.metadata.get('hw_firmware_type') == 'uefi':
#node['capabilities'] += ",boot_mode:uefi"
if not cache.get(baremetal.image['id']):
cache[baremetal.image['id']] = glance.images.get(baremetal.image['id'])
image = cache.get(baremetal.image['id'])
if image.get('hw_firmware_type') == 'uefi':
node['capabilities'] += ",boot_mode:uefi"
bm_name_end = baremetal.name[len(baremetal_base):]
if '-' in bm_name_end:
@ -240,9 +244,10 @@ def _write_pairs(bmc_bm_pairs):
def main():
args = _parse_args()
bmc_base, baremetal_base, provision_net, undercloud_name = _get_names(args)
nova, neutron = _get_clients()
nova, neutron, glance = _get_clients()
bmc_ports, bm_ports = _get_ports(neutron, bmc_base, baremetal_base)
nodes, bmc_bm_pairs, extra_nodes = _build_nodes(nova, bmc_ports, bm_ports,
nodes, bmc_bm_pairs, extra_nodes = _build_nodes(nova, glance, bmc_ports,
bm_ports,
provision_net,
baremetal_base,
undercloud_name)

View File

@ -140,35 +140,7 @@ def _get_heat_client():
import os_client_config
return os_client_config.make_client('orchestration', cloud=cloud)
else:
auth_data = auth._create_auth_parameters()
username = auth_data['os_user']
password = auth_data['os_password']
tenant = auth_data['os_tenant']
auth_url = auth_data['os_auth_url']
project = auth_data['os_project']
user_domain = auth_data['os_user_domain']
project_domain = auth_data['os_project_domain']
# Get token for Heat to use
if '/v3' not in auth_url:
token_data = auth._get_keystone_token()
token_id = token_data['token']['id']
catalog_key = 'serviceCatalog'
else:
token_data = auth._get_keystone_token()
token_id = token_data['auth_token']
catalog_key = 'catalog'
# Get Heat endpoint
for endpoint in token_data[catalog_key]:
if endpoint['name'] == 'heat':
try:
# TODO: What if there's more than one endpoint?
heat_endpoint = endpoint['endpoints'][0]['publicURL']
except KeyError:
# Keystone v3 endpoint data looks different
heat_endpoint = [e for e in endpoint['endpoints']
if e['interface'] == 'public'][0]['url']
token_id, heat_endpoint = auth._get_token_and_endpoint('heat')
return heat_client.Client('1', endpoint=heat_endpoint, token=token_id)
def _deploy(stack_name, stack_template, env_path, poll):

View File

@ -190,6 +190,26 @@ class TestCreateAuthParameters(testtools.TestCase):
self.assertEqual(expected, result)
V2_TOKEN_DATA = {'token': {'id': 'fake_token'},
'serviceCatalog': [{'name': 'nova'},
{'name': 'heat',
'endpoints': [
{'publicURL': 'heat_endpoint'}
]
}
]}
V3_TOKEN_DATA = {'auth_token': 'fake_v3_token',
'catalog': [{'name': 'nova'},
{'name': 'heat',
'endpoints': [
{'interface': 'private'},
{'interface': 'public',
'url': 'heat_endpoint'}
]
}
]}
class TestKeystoneAuth(testtools.TestCase):
@mock.patch('keystoneauth1.session.Session')
@mock.patch('keystoneauth1.identity.v3.Password')
@ -289,3 +309,37 @@ class TestKeystoneAuth(testtools.TestCase):
user_domain_name='default',
project_domain_name='default'
)
@mock.patch('openstack_virtual_baremetal.auth._get_keystone_token')
@mock.patch('openstack_virtual_baremetal.auth._create_auth_parameters')
def test_get_token_and_endpoint_v2(self, mock_cap, mock_gkt):
fake_auth_data = {'os_user': 'admin',
'os_password': 'password',
'os_tenant': 'admin',
'os_auth_url': 'auth',
'os_project': '',
'os_user_domain': '',
'os_project_domain': '',
}
mock_cap.return_value = fake_auth_data
mock_gkt.return_value = V2_TOKEN_DATA
token, endpoint = auth._get_token_and_endpoint('heat')
self.assertEqual('fake_token', token)
self.assertEqual('heat_endpoint', endpoint)
@mock.patch('openstack_virtual_baremetal.auth._get_keystone_token')
@mock.patch('openstack_virtual_baremetal.auth._create_auth_parameters')
def test_get_token_and_endpoint_v3(self, mock_cap, mock_gkt):
fake_auth_data = {'os_user': 'admin',
'os_password': 'password',
'os_tenant': '',
'os_auth_url': 'auth/v3',
'os_project': 'admin',
'os_user_domain': 'default',
'os_project_domain': 'default',
}
mock_cap.return_value = fake_auth_data
mock_gkt.return_value = V3_TOKEN_DATA
token, endpoint = auth._get_token_and_endpoint('heat')
self.assertEqual('fake_v3_token', token)
self.assertEqual('heat_endpoint', endpoint)

View File

@ -161,10 +161,12 @@ class TestBuildNodesJson(testtools.TestCase):
self.useFixture(fixtures.EnvironmentVariable('OS_CLOUD', 'foo'))
build_nodes_json._get_clients()
calls = [mock.call('compute', cloud='foo'),
mock.call('network', cloud='foo')]
mock.call('network', cloud='foo'),
mock.call('image', cloud='foo')]
self.assertEqual(calls, mock_make_client.mock_calls)
def _test_get_clients_env(self, mock_nova, mock_neutron):
def _test_get_clients_env(self, mock_nova, mock_neutron, mock_glance,
mock_gtae):
self.useFixture(fixtures.EnvironmentVariable('OS_USERNAME', 'admin'))
self.useFixture(fixtures.EnvironmentVariable('OS_PASSWORD', 'pw'))
self.useFixture(fixtures.EnvironmentVariable('OS_TENANT_NAME',
@ -174,28 +176,45 @@ class TestBuildNodesJson(testtools.TestCase):
mock_nova.return_value = mock_nova_client
mock_neutron_client = mock.Mock()
mock_neutron.return_value = mock_neutron_client
nova, neutron = build_nodes_json._get_clients()
mock_glance_client = mock.Mock()
mock_glance.return_value = mock_glance_client
mock_token = 'abc-123'
mock_endpoint = 'glance://endpoint'
mock_gtae.return_value = (mock_token, mock_endpoint)
nova, neutron, glance = build_nodes_json._get_clients()
self.assertEqual(mock_nova_client, nova)
self.assertEqual(mock_neutron_client, neutron)
self.assertEqual(mock_glance_client, glance)
@mock.patch('openstack_virtual_baremetal.auth._get_token_and_endpoint')
@mock.patch('openstack_virtual_baremetal.build_nodes_json.nc.__version__',
('6', '0', '0'))
@mock.patch('glanceclient.Client')
@mock.patch('neutronclient.v2_0.client.Client')
@mock.patch('novaclient.client.Client')
def test_get_clients_env_6(self, mock_nova, mock_neutron):
self._test_get_clients_env(mock_nova, mock_neutron)
def test_get_clients_env_6(self, mock_nova, mock_neutron, mock_glance,
mock_gtae):
self._test_get_clients_env(mock_nova, mock_neutron, mock_glance,
mock_gtae)
@mock.patch('openstack_virtual_baremetal.auth._get_token_and_endpoint')
@mock.patch('openstack_virtual_baremetal.build_nodes_json.nc.__version__',
('7', '0', '0'))
@mock.patch('glanceclient.Client')
@mock.patch('neutronclient.v2_0.client.Client')
@mock.patch('novaclient.client.Client')
def test_get_clients_env_7(self, mock_nova, mock_neutron):
self._test_get_clients_env(mock_nova, mock_neutron)
def test_get_clients_env_7(self, mock_nova, mock_neutron, mock_glance,
mock_gtae):
self._test_get_clients_env(mock_nova, mock_neutron, mock_glance,
mock_gtae)
@mock.patch('openstack_virtual_baremetal.auth._get_token_and_endpoint')
@mock.patch('openstack_virtual_baremetal.auth._get_keystone_session')
@mock.patch('glanceclient.Client')
@mock.patch('neutronclient.v2_0.client.Client')
@mock.patch('novaclient.client.Client')
def test_get_clients_env_v3(self, mock_nova, mock_neutron, mock_gks):
def test_get_clients_env_v3(self, mock_nova, mock_neutron, mock_glance,
mock_gks, mock_gtae):
self.useFixture(fixtures.EnvironmentVariable('OS_USERNAME', 'admin'))
self.useFixture(fixtures.EnvironmentVariable('OS_PASSWORD', 'pw'))
self.useFixture(fixtures.EnvironmentVariable('OS_PROJECT_NAME',
@ -209,12 +228,20 @@ class TestBuildNodesJson(testtools.TestCase):
mock_nova.return_value = mock_nova_client
mock_neutron_client = mock.Mock()
mock_neutron.return_value = mock_neutron_client
mock_glance_client = mock.Mock()
mock_glance.return_value = mock_glance_client
mock_session_inst = mock.Mock()
mock_gks.return_value = mock_session_inst
nova, neutron = build_nodes_json._get_clients()
mock_token = 'abc-123'
mock_endpoint = 'glance://endpoint'
mock_gtae.return_value = (mock_token, mock_endpoint)
nova, neutron, glance = build_nodes_json._get_clients()
mock_neutron.assert_called_once_with(session=mock_session_inst)
mock_glance.assert_called_once_with('2', token=mock_token,
endpoint=mock_endpoint)
self.assertEqual(mock_nova_client, nova)
self.assertEqual(mock_neutron_client, neutron)
self.assertEqual(mock_glance_client, glance)
def test_get_ports(self):
neutron = mock.Mock()
@ -296,8 +323,10 @@ class TestBuildNodesJson(testtools.TestCase):
ips_return_val = 'ips call value'
nova.servers.ips.return_value = ips_return_val
glance = mock.Mock()
nodes, bmc_bm_pairs, extra_nodes = build_nodes_json._build_nodes(
nova, bmc_ports, bm_ports, 'provision', 'bm', 'undercloud')
nova, glance, bmc_ports, bm_ports, 'provision', 'bm', 'undercloud')
expected_nodes = TEST_NODES
self.assertEqual(expected_nodes, nodes)
self.assertEqual([('1.1.1.1', 'bm_0'), ('1.1.1.2', 'bm_1')],
@ -317,12 +346,14 @@ class TestBuildNodesJson(testtools.TestCase):
servers[1].name = 'bm-foo-control_1'
ips_return_val = 'ips call value'
nova.servers.ips.return_value = ips_return_val
glance = mock.Mock()
mock_image_get = mock.Mock()
nova.images.get.return_value = mock_image_get
mock_image_get.metadata.get.return_value = 'uefi'
mock_image_get.get.return_value = 'uefi'
glance.images.get.return_value = mock_image_get
nodes, bmc_bm_pairs, extra_nodes = build_nodes_json._build_nodes(
nova, bmc_ports, bm_ports, 'provision', 'bm-foo', None)
nova, glance, bmc_ports, bm_ports, 'provision', 'bm-foo', None)
expected_nodes = copy.deepcopy(TEST_NODES)
expected_nodes[0]['name'] = 'bm-foo-control-0'
expected_nodes[0]['capabilities'] = ('boot_option:local,'
@ -396,7 +427,8 @@ class TestBuildNodesJson(testtools.TestCase):
undercloud_name)
nova = mock.Mock()
neutron = mock.Mock()
mock_get_clients.return_value = (nova, neutron)
glance = mock.Mock()
mock_get_clients.return_value = (nova, neutron, glance)
bmc_ports = mock.Mock()
bm_ports = mock.Mock()
mock_get_ports.return_value = (bmc_ports, bm_ports)
@ -412,8 +444,9 @@ class TestBuildNodesJson(testtools.TestCase):
mock_get_clients.assert_called_once_with()
mock_get_ports.assert_called_once_with(neutron, bmc_base,
baremetal_base)
mock_build_nodes.assert_called_once_with(nova, bmc_ports, bm_ports,
provision_net, baremetal_base,
mock_build_nodes.assert_called_once_with(nova, glance, bmc_ports,
bm_ports, provision_net,
baremetal_base,
undercloud_name)
mock_write_nodes.assert_called_once_with(nodes, extra_nodes, args)
mock_write_pairs.assert_called_once_with(pairs)

View File

@ -371,26 +371,6 @@ class TestDeploy(testtools.TestCase):
deploy._validate_env(args, 'foo.yaml')
V2_TOKEN_DATA = {'token': {'id': 'fake_token'},
'serviceCatalog': [{'name': 'nova'},
{'name': 'heat',
'endpoints': [
{'publicURL': 'heat_endpoint'}
]
}
]}
V3_TOKEN_DATA = {'auth_token': 'fake_v3_token',
'catalog': [{'name': 'nova'},
{'name': 'heat',
'endpoints': [
{'interface': 'private'},
{'interface': 'public',
'url': 'heat_endpoint'}
]
}
]}
class TestGetHeatClient(testtools.TestCase):
@mock.patch('os_client_config.make_client')
def test_os_cloud(self, mock_make_client):
@ -399,41 +379,13 @@ class TestGetHeatClient(testtools.TestCase):
mock_make_client.assert_called_once_with('orchestration', cloud='foo')
@mock.patch('heatclient.client.Client')
@mock.patch('openstack_virtual_baremetal.auth._get_keystone_token')
@mock.patch('openstack_virtual_baremetal.auth._create_auth_parameters')
def test_keystone_v2(self, mock_cap, mock_gkt, mock_hc):
fake_auth_data = {'os_user': 'admin',
'os_password': 'password',
'os_tenant': 'admin',
'os_auth_url': 'auth',
'os_project': '',
'os_user_domain': '',
'os_project_domain': '',
}
mock_cap.return_value = fake_auth_data
mock_gkt.return_value = V2_TOKEN_DATA
@mock.patch('openstack_virtual_baremetal.auth._get_token_and_endpoint')
def test_heatclient(self, mock_gtae, mock_hc):
mock_gtae.return_value = ('fake_token', 'heat_endpoint')
deploy._get_heat_client()
mock_hc.assert_called_once_with('1', endpoint='heat_endpoint',
token='fake_token')
@mock.patch('heatclient.client.Client')
@mock.patch('openstack_virtual_baremetal.auth._get_keystone_token')
@mock.patch('openstack_virtual_baremetal.auth._create_auth_parameters')
def test_keystone_v3(self, mock_cap, mock_gkt, mock_hc):
fake_auth_data = {'os_user': 'admin',
'os_password': 'password',
'os_tenant': 'admin',
'os_auth_url': 'auth/v3',
'os_project': 'admin',
'os_user_domain': 'default',
'os_project_domain': 'default',
}
mock_cap.return_value = fake_auth_data
mock_gkt.return_value = V3_TOKEN_DATA
deploy._get_heat_client()
mock_hc.assert_called_once_with('1', endpoint='heat_endpoint',
token='fake_v3_token')
if __name__ == '__main__':
unittest.main()

View File

@ -1,6 +1,7 @@
os-client-config
pyghmi
PyYAML
python-glanceclient
python-heatclient
python-keystoneclient
python-neutronclient