Add Metalsmith client

In OSP-17 and newer, there is no Nova running as undercloud
service and instead of Nova client, Metalsmith client has to be
used to e.g. get overcloud nodes and its IP addresses.

This adds Metalsmith client with Metalsmith client support

Change-Id: Iaeef3e87a29513e97f1702085928aa05e1aa82ad
This commit is contained in:
Federico Ressi 2022-07-05 12:51:01 +02:00
parent 24230f8ecf
commit 682011c1f6
8 changed files with 250 additions and 1 deletions

View File

@ -5,6 +5,7 @@ docker==4.4.1
fixtures==3.0.0
Jinja2==2.11.2
keystoneauth1==4.3.0
metalsmith==1.6.2
mock==3.0.5
netaddr==0.8.0
neutron-lib==2.7.0

View File

@ -6,6 +6,7 @@ docker>=4.4.1 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
Jinja2>=2.11.2 # BSD
keystoneauth1>=4.3.0 # Apache-2.0
metalsmith>=1.6.2 # Apache-2.0
netaddr>=0.8.0 # BSD
neutron-lib>=2.7.0 # Apache-2.0
oslo.config>=8.4.0 # Apache-2.0

View File

@ -0,0 +1,31 @@
# Copyright 2022 Red Hat
#
# 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.
from __future__ import absolute_import
from tobiko.openstack.metalsmith import _client
from tobiko.openstack.metalsmith import _instance
CLIENT_CLASSES = _client.CLIENT_CLASSES
MetalsmithClient = _client.MetalsmithClient
MetalsmithClientFixture = _client.MetalsmithClientFixture
MetalsmithClientType = _client.MetalsmithClientType
metalsmith_client = _client.metalsmith_client
get_metalsmith_client = _client.get_metalsmith_client
MetalsmithInstance = _instance.MetalsmithInstance
list_instances = _instance.list_instances
find_instance = _instance.find_instance
list_instance_ip_addresses = _instance.list_instance_ip_addresses
find_instance_ip_address = _instance.find_instance_ip_address

View File

@ -0,0 +1,88 @@
# Copyright 2022 Red Hat
#
# 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.
from __future__ import absolute_import
import typing
import metalsmith
from oslo_log import log
import tobiko
from tobiko.openstack import keystone
from tobiko.openstack import _client
LOG = log.getLogger(__name__)
CLIENT_CLASSES = (metalsmith.Provisioner,)
MetalsmithClient = typing.Union[metalsmith.Provisioner]
class MetalsmithClientFixture(_client.OpenstackClientFixture):
def init_client(self, session) -> MetalsmithClient:
return metalsmith.Provisioner(session=session)
class MetalsmithClientManager(_client.OpenstackClientManager):
def create_client(self, session) -> MetalsmithClientFixture:
return MetalsmithClientFixture(session=session)
CLIENTS = MetalsmithClientManager()
def metalsmith_client_manager(manager: MetalsmithClientManager = None) \
-> MetalsmithClientManager:
if manager is None:
manager = CLIENTS
return manager
MetalsmithClientType = typing.Union[
MetalsmithClient,
MetalsmithClientFixture,
typing.Type[MetalsmithClientFixture]]
def metalsmith_client(obj: MetalsmithClientType = None) \
-> MetalsmithClient:
if obj is None:
return get_metalsmith_client()
if isinstance(obj, CLIENT_CLASSES):
return obj
fixture = tobiko.setup_fixture(obj)
if isinstance(fixture, MetalsmithClientFixture):
assert fixture.client is not None
return fixture.client
message = f"Object '{obj}' is not a MetalsmithProvisionerFixture"
raise TypeError(message)
def get_metalsmith_client(session: keystone.KeystoneSessionType = None,
shared=True,
init_client=None,
manager: MetalsmithClientManager = None) \
-> MetalsmithClient:
manager = metalsmith_client_manager(manager)
client = manager.get_client(session=session,
shared=shared,
init_client=init_client)
tobiko.setup_fixture(client)
return client.client

View File

@ -0,0 +1,81 @@
# Copyright 2022 Red Hat
#
# 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.
from __future__ import absolute_import
import typing
import metalsmith
import netaddr
import tobiko
from tobiko.shell import ping
from tobiko.shell import ssh
from tobiko.openstack.metalsmith import _client
MetalsmithInstance = typing.Union[metalsmith.Instance]
def list_instances(client: _client.MetalsmithClientType = None,
**params) \
-> tobiko.Selection[MetalsmithInstance]:
instances = tobiko.select(
_client.metalsmith_client(client).list_instances())
if params:
instances = instances.with_attributes(**params)
return instances
def find_instance(client: _client.MetalsmithClientType = None,
unique=False, **params) -> MetalsmithInstance:
servers = list_instances(client=client, **params)
if unique:
return servers.unique
else:
return servers.first
def list_instance_ip_addresses(instance: MetalsmithInstance,
ip_version: int = None,
network_name: str = None,
check_connectivity=False,
ssh_client: ssh.SSHClientType = None)\
-> tobiko.Selection[netaddr.IPAddress]:
ip_addresses = tobiko.Selection[netaddr.IPAddress]()
for _network, addresses in instance.ip_addresses().items():
if network_name not in [None, _network]:
continue
for address in addresses:
ip_address = netaddr.IPAddress(address)
if ip_version not in [None, ip_address.version]:
continue
ip_addresses.append(ip_address)
# check ICMP connectivity
if check_connectivity:
ip_addresses = ping.list_reachable_hosts(
ip_addresses, ssh_client=ssh_client)
return ip_addresses
def find_instance_ip_address(instance: MetalsmithInstance,
unique=False,
**params) -> netaddr.IPAddress:
addresses = list_instance_ip_addresses(instance=instance, **params)
if unique:
return addresses.unique
else:
return addresses.first

View File

@ -0,0 +1,47 @@
# Copyright 2022 Red Hat
#
# 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.
from __future__ import absolute_import
from tobiko.openstack import keystone
from tobiko.openstack import metalsmith
from tobiko.tests.unit import openstack
from tobiko.tests.unit.openstack import test_client
class MetalsmithClientFixtureTest(test_client.OpenstackClientFixtureTest):
def create_client(self, session=None):
return metalsmith.MetalsmithClientFixture(session=session)
class GetMetalsmithClientTest(openstack.OpenstackTest):
def test_get_metalsmith_client(self, session=None, shared=True):
client1 = metalsmith.get_metalsmith_client(
session=session, shared=shared)
client2 = metalsmith.get_metalsmith_client(
session=session, shared=shared)
if shared:
self.assertIs(client1, client2)
else:
self.assertIsNot(client1, client2)
self.assertIsInstance(client1, metalsmith.CLIENT_CLASSES)
self.assertIsInstance(client2, metalsmith.CLIENT_CLASSES)
def test_get_metalsmith_client_with_not_shared(self):
self.test_get_metalsmith_client(shared=False)
def test_get_metalsmith_client_with_session(self):
session = keystone.get_keystone_session()
self.test_get_metalsmith_client(session=session)

View File

@ -192,7 +192,7 @@ Mako===1.1.6
marathon===0.13.0
MarkupSafe===2.0.1
mbstrdecoder===1.1.0
metalsmith===1.6.1
metalsmith===1.6.2
microversion-parse===1.0.1
mistral-lib===2.5.0
mitba===1.1.1