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-octaviaclient.git (Load-balancer service)
|
||||||
* https://opendev.org/openstack/python-senlinclient (Clustering service)
|
* https://opendev.org/openstack/python-senlinclient (Clustering service)
|
||||||
* https://opendev.org/openstack/python-vitrageclient.git (RCA 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 '
|
'this limitation, any nova feature supported with '
|
||||||
'microversion number above max_nova_api_microversion '
|
'microversion number above max_nova_api_microversion '
|
||||||
'will not be available.')),
|
'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',
|
cfg.IntOpt('event_purge_batch_size',
|
||||||
min=1,
|
min=1,
|
||||||
default=200,
|
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-editor==1.0.3
|
||||||
python-glanceclient==2.8.0
|
python-glanceclient==2.8.0
|
||||||
python-heatclient==1.10.0
|
python-heatclient==1.10.0
|
||||||
|
python-ironicclient==2.8.0
|
||||||
python-keystoneclient==3.8.0
|
python-keystoneclient==3.8.0
|
||||||
python-magnumclient==2.3.0
|
python-magnumclient==2.3.0
|
||||||
python-manilaclient==1.16.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-designateclient>=2.7.0 # Apache-2.0
|
||||||
python-glanceclient>=2.8.0 # Apache-2.0
|
python-glanceclient>=2.8.0 # Apache-2.0
|
||||||
python-heatclient>=1.10.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-keystoneclient>=3.8.0 # Apache-2.0
|
||||||
python-magnumclient>=2.3.0 # Apache-2.0
|
python-magnumclient>=2.3.0 # Apache-2.0
|
||||||
python-manilaclient>=1.16.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
|
designate = heat.engine.clients.os.designate:DesignateClientPlugin
|
||||||
glance = heat.engine.clients.os.glance:GlanceClientPlugin
|
glance = heat.engine.clients.os.glance:GlanceClientPlugin
|
||||||
heat = heat.engine.clients.os.heat_plugin:HeatClientPlugin
|
heat = heat.engine.clients.os.heat_plugin:HeatClientPlugin
|
||||||
|
ironic = heat.engine.clients.os.ironic:IronicClientPlugin
|
||||||
keystone = heat.engine.clients.os.keystone:KeystoneClientPlugin
|
keystone = heat.engine.clients.os.keystone:KeystoneClientPlugin
|
||||||
magnum = heat.engine.clients.os.magnum:MagnumClientPlugin
|
magnum = heat.engine.clients.os.magnum:MagnumClientPlugin
|
||||||
manila = heat.engine.clients.os.manila:ManilaClientPlugin
|
manila = heat.engine.clients.os.manila:ManilaClientPlugin
|
||||||
@ -174,6 +175,9 @@ heat.constraints =
|
|||||||
senlin.profile_type = heat.engine.clients.os.senlin:ProfileTypeConstraint
|
senlin.profile_type = heat.engine.clients.os.senlin:ProfileTypeConstraint
|
||||||
trove.flavor = heat.engine.clients.os.trove:FlavorConstraint
|
trove.flavor = heat.engine.clients.os.trove:FlavorConstraint
|
||||||
zaqar.queue = heat.engine.clients.os.zaqar:QueueConstraint
|
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 =
|
heat.stack_lifecycle_plugins =
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user