Add ironic client plugin support
Change-Id: I2f3ee94424c4dab75fbcef6f8b32e565b45684e4 Task: 36285
This commit is contained in:
parent
6a2a4000b9
commit
5b35225d16
@ -76,3 +76,4 @@ We have integration with
|
||||
* https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service)
|
||||
* https://opendev.org/openstack/python-senlinclient (Clustering service)
|
||||
* https://opendev.org/openstack/python-vitrageclient.git (RCA service)
|
||||
* https://opendev.org/openstack/python-ironicclient (baremetal provisioning service)
|
||||
|
@ -183,6 +183,11 @@ engine_opts = [
|
||||
'this limitation, any nova feature supported with '
|
||||
'microversion number above max_nova_api_microversion '
|
||||
'will not be available.')),
|
||||
cfg.FloatOpt('max_ironic_api_microversion',
|
||||
help=_('Maximum ironic API version for client plugin. With '
|
||||
'this limitation, any ironic feature supported with '
|
||||
'microversion number above '
|
||||
'max_ironic_api_microversion will not be available.')),
|
||||
cfg.IntOpt('event_purge_batch_size',
|
||||
min=1,
|
||||
default=200,
|
||||
|
82
heat/engine/clients/os/ironic.py
Normal file
82
heat/engine/clients/os/ironic.py
Normal file
@ -0,0 +1,82 @@
|
||||
#
|
||||
# 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 ironicclient.common.apiclient import exceptions as ic_exc
|
||||
from ironicclient.v1 import client as ironic_client
|
||||
from oslo_config import cfg
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine.clients import client_plugin
|
||||
from heat.engine import constraints
|
||||
|
||||
CLIENT_NAME = 'ironic'
|
||||
|
||||
|
||||
class IronicClientPlugin(client_plugin.ClientPlugin):
|
||||
|
||||
service_types = [BAREMETAL] = ['baremetal']
|
||||
IRONIC_API_VERSION = 1.58
|
||||
max_ironic_api_microversion = cfg.CONF.max_ironic_api_microversion
|
||||
max_microversion = max_ironic_api_microversion if (
|
||||
max_ironic_api_microversion is not None and (
|
||||
IRONIC_API_VERSION > max_ironic_api_microversion)
|
||||
) else IRONIC_API_VERSION
|
||||
|
||||
def _create(self):
|
||||
interface = self._get_client_option(CLIENT_NAME, 'endpoint_type')
|
||||
args = {
|
||||
'interface': interface,
|
||||
'service_type': self.BAREMETAL,
|
||||
'session': self.context.keystone_session,
|
||||
'region_name': self._get_region_name(),
|
||||
'os_ironic_api_version': self.max_microversion
|
||||
}
|
||||
client = ironic_client.Client(**args)
|
||||
return client
|
||||
|
||||
def is_not_found(self, ex):
|
||||
return isinstance(ex, ic_exc.NotFound)
|
||||
|
||||
def is_over_limit(self, ex):
|
||||
return isinstance(ex, ic_exc.RequestEntityTooLarge)
|
||||
|
||||
def is_conflict(self, ex):
|
||||
return isinstance(ex, ic_exc.Conflict)
|
||||
|
||||
def _get_rsrc_name_or_id(self, value, entity, entity_msg):
|
||||
entity_client = getattr(self.client(), entity)
|
||||
try:
|
||||
return entity_client.get(value).uuid
|
||||
except ic_exc.NotFound:
|
||||
# Ironic cli will find the value either is name or id,
|
||||
# so no need to call list() here.
|
||||
raise exception.EntityNotFound(entity=entity_msg,
|
||||
name=value)
|
||||
|
||||
def get_portgroup(self, value):
|
||||
return self._get_rsrc_name_or_id(value, entity='portgroup',
|
||||
entity_msg='PortGroup')
|
||||
|
||||
def get_node(self, value):
|
||||
return self._get_rsrc_name_or_id(value, entity='node',
|
||||
entity_msg='Node')
|
||||
|
||||
|
||||
class PortGroupConstraint(constraints.BaseCustomConstraint):
|
||||
resource_client_name = CLIENT_NAME
|
||||
resource_getter_name = 'get_portgroup'
|
||||
|
||||
|
||||
class NodeConstraint(constraints.BaseCustomConstraint):
|
||||
resource_client_name = CLIENT_NAME
|
||||
resource_getter_name = 'get_node'
|
77
heat/tests/clients/test_ironic_client.py
Normal file
77
heat/tests/clients/test_ironic_client.py
Normal file
@ -0,0 +1,77 @@
|
||||
#
|
||||
# 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 ironicclient import exceptions as ic_exc
|
||||
import mock
|
||||
|
||||
from heat.engine.clients.os import ironic as ic
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
class IronicClientPluginTest(common.HeatTestCase):
|
||||
|
||||
def test_create(self):
|
||||
context = utils.dummy_context()
|
||||
plugin = context.clients.client_plugin('ironic')
|
||||
client = plugin.client()
|
||||
self.assertEqual('http://server.test:5000/v3',
|
||||
client.port.api.session.auth.endpoint)
|
||||
|
||||
|
||||
class fake_resource(object):
|
||||
def __init__(self, id=None, name=None):
|
||||
self.uuid = id
|
||||
self.name = name
|
||||
|
||||
|
||||
class PortGroupConstraintTest(common.HeatTestCase):
|
||||
def setUp(self):
|
||||
super(PortGroupConstraintTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.mock_port_group_get = mock.Mock()
|
||||
self.ctx.clients.client_plugin(
|
||||
'ironic').client().portgroup.get = self.mock_port_group_get
|
||||
self.constraint = ic.PortGroupConstraint()
|
||||
|
||||
def test_validate(self):
|
||||
self.mock_port_group_get.return_value = fake_resource(
|
||||
id='my_port_group')
|
||||
self.assertTrue(self.constraint.validate(
|
||||
'my_port_group', self.ctx))
|
||||
|
||||
def test_validate_fail(self):
|
||||
self.mock_port_group_get.side_effect = ic_exc.NotFound()
|
||||
self.assertFalse(self.constraint.validate(
|
||||
"bad_port_group", self.ctx))
|
||||
|
||||
|
||||
class NodeConstraintTest(common.HeatTestCase):
|
||||
def setUp(self):
|
||||
super(NodeConstraintTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.mock_node_get = mock.Mock()
|
||||
self.ctx.clients.client_plugin(
|
||||
'ironic').client().node.get = self.mock_node_get
|
||||
self.constraint = ic.NodeConstraint()
|
||||
|
||||
def test_validate(self):
|
||||
self.mock_node_get.return_value = fake_resource(
|
||||
id='my_node')
|
||||
self.assertTrue(self.constraint.validate(
|
||||
'my_node', self.ctx))
|
||||
|
||||
def test_validate_fail(self):
|
||||
self.mock_node_get.side_effect = ic_exc.NotFound()
|
||||
self.assertFalse(self.constraint.validate(
|
||||
"bad_node", self.ctx))
|
0
heat/tests/openstack/ironic/__init__.py
Normal file
0
heat/tests/openstack/ironic/__init__.py
Normal file
@ -111,6 +111,7 @@ python-designateclient==2.7.0
|
||||
python-editor==1.0.3
|
||||
python-glanceclient==2.8.0
|
||||
python-heatclient==1.10.0
|
||||
python-ironicclient==2.8.0
|
||||
python-keystoneclient==3.8.0
|
||||
python-magnumclient==2.3.0
|
||||
python-manilaclient==1.16.0
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Introduce a Ironic client plugin module that will be used by the Ironic's
|
||||
resources.
|
||||
Support only ironicclient version >=2.8.0 to get allocation functionality
|
||||
support.
|
@ -38,6 +38,7 @@ python-cinderclient>=3.3.0 # Apache-2.0
|
||||
python-designateclient>=2.7.0 # Apache-2.0
|
||||
python-glanceclient>=2.8.0 # Apache-2.0
|
||||
python-heatclient>=1.10.0 # Apache-2.0
|
||||
python-ironicclient>=2.8.0 # Apache-2.0
|
||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
python-magnumclient>=2.3.0 # Apache-2.0
|
||||
python-manilaclient>=1.16.0 # Apache-2.0
|
||||
|
@ -74,6 +74,7 @@ heat.clients =
|
||||
designate = heat.engine.clients.os.designate:DesignateClientPlugin
|
||||
glance = heat.engine.clients.os.glance:GlanceClientPlugin
|
||||
heat = heat.engine.clients.os.heat_plugin:HeatClientPlugin
|
||||
ironic = heat.engine.clients.os.ironic:IronicClientPlugin
|
||||
keystone = heat.engine.clients.os.keystone:KeystoneClientPlugin
|
||||
magnum = heat.engine.clients.os.magnum:MagnumClientPlugin
|
||||
manila = heat.engine.clients.os.manila:ManilaClientPlugin
|
||||
@ -174,6 +175,9 @@ heat.constraints =
|
||||
senlin.profile_type = heat.engine.clients.os.senlin:ProfileTypeConstraint
|
||||
trove.flavor = heat.engine.clients.os.trove:FlavorConstraint
|
||||
zaqar.queue = heat.engine.clients.os.zaqar:QueueConstraint
|
||||
#ironic
|
||||
ironic.portgroup = heat.engine.clients.os.ironic:PortGroupConstraint
|
||||
ironic.node = heat.engine.clients.os.ironic:NodeConstraint
|
||||
|
||||
heat.stack_lifecycle_plugins =
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user