summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Baker <sbaker@redhat.com>2019-09-23 05:16:05 +0000
committerSteve Baker <sbaker@redhat.com>2019-10-31 08:55:39 +1300
commit7ebad2e34427084af932ba211e11a1f5af40a881 (patch)
tree699e17f72ecd75402b37ec4067c1053877cf15db
parentb462ca420bebba2d0eaa374b74ebdeaf831ea181 (diff)
downloadironic-7ebad2e34427084af932ba211e11a1f5af40a881.tar.gz
Allow vendor_data to be included in a configdrive dict
configdrive can contain a vendor_data2.json file containing key/value pairs injected by nova's vendordata mechanism[1]. This change lets Ironic accept a vendor_data key when configdrive is provided as json, allowing parity with nova. This change requires an openstacksdk release 0.37.0 [1] https://www.madebymikal.com/nova-vendordata-deployment-an-excessively-detailed-guide/ Change-Id: Id990b970619a113c5d5ead47fb550870d91b5e04 Task: 36756 Story: 2006597 Blueprint: nova-less-deploy
-rw-r--r--api-ref/source/parameters.yaml1
-rw-r--r--doc/source/contributor/webapi-version-history.rst6
-rw-r--r--ironic/api/controllers/v1/utils.py35
-rw-r--r--ironic/api/controllers/v1/versions.py4
-rw-r--r--ironic/common/release_mappings.py2
-rw-r--r--ironic/conductor/utils.py5
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_node.py5
-rw-r--r--ironic/tests/unit/api/controllers/v1/test_utils.py23
-rw-r--r--ironic/tests/unit/conductor/test_manager.py31
-rw-r--r--lower-constraints.txt2
-rw-r--r--releasenotes/notes/configdrive-vendordata-122049bd7c6e1b67.yaml8
-rw-r--r--requirements.txt2
12 files changed, 97 insertions, 27 deletions
diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml
index fd88e8736..8707d111a 100644
--- a/api-ref/source/parameters.yaml
+++ b/api-ref/source/parameters.yaml
@@ -561,6 +561,7 @@ configdrive:
* ``network_data`` (optional) - JSON object with networking configuration.
* ``user_data`` (optional) - user data. May be a string (which will be
UTF-8 encoded); a JSON object, or a JSON array.
+ * ``vendor_data`` (optional) - JSON object with extra vendor data.
This parameter is only accepted when setting the state to "active" or
"rebuild".
diff --git a/doc/source/contributor/webapi-version-history.rst b/doc/source/contributor/webapi-version-history.rst
index 338539e9e..6b18ba8d1 100644
--- a/doc/source/contributor/webapi-version-history.rst
+++ b/doc/source/contributor/webapi-version-history.rst
@@ -2,6 +2,12 @@
REST API Version History
========================
+1.59 (Ussuri, master)
+
+Added the ability to specify a ``vendor_data`` dictionary field in the
+``configdrive`` parameter submitted with the deployment of a node. The value
+is a dictionary which is served as ``vendor_data2.json`` in the config drive.
+
1.58 (Train, 12.2.0)
--------------------
diff --git a/ironic/api/controllers/v1/utils.py b/ironic/api/controllers/v1/utils.py
index 2949325fc..98a6a1462 100644
--- a/ironic/api/controllers/v1/utils.py
+++ b/ironic/api/controllers/v1/utils.py
@@ -616,7 +616,8 @@ _CONFIG_DRIVE_SCHEMA = {
'network_data': {'type': 'object'},
'user_data': {
'type': ['object', 'array', 'string', 'null']
- }
+ },
+ 'vendor_data': {'type': 'object'},
},
'additionalProperties': False
},
@@ -648,13 +649,22 @@ def check_allow_configdrive(target, configdrive=None):
raise wsme.exc.ClientSideError(
msg, status_code=http_client.BAD_REQUEST)
- if isinstance(configdrive, dict) and not allow_build_configdrive():
- msg = _('Providing a JSON object for configdrive is only supported'
- ' starting with API version %(base)s.%(opr)s') % {
- 'base': versions.BASE_VERSION,
- 'opr': versions.MINOR_56_BUILD_CONFIGDRIVE}
- raise wsme.exc.ClientSideError(
- msg, status_code=http_client.BAD_REQUEST)
+ if isinstance(configdrive, dict):
+ if not allow_build_configdrive():
+ msg = _('Providing a JSON object for configdrive is only supported'
+ ' starting with API version %(base)s.%(opr)s') % {
+ 'base': versions.BASE_VERSION,
+ 'opr': versions.MINOR_56_BUILD_CONFIGDRIVE}
+ raise wsme.exc.ClientSideError(
+ msg, status_code=http_client.BAD_REQUEST)
+ if ('vendor_data' in configdrive and
+ not allow_configdrive_vendor_data()):
+ msg = _('Providing vendor_data in configdrive is only supported'
+ ' starting with API version %(base)s.%(opr)s') % {
+ 'base': versions.BASE_VERSION,
+ 'opr': versions.MINOR_59_CONFIGDRIVE_VENDOR_DATA}
+ raise wsme.exc.ClientSideError(
+ msg, status_code=http_client.BAD_REQUEST)
def check_allow_filter_by_fault(fault):
@@ -1163,6 +1173,15 @@ def allow_build_configdrive():
return api.request.version.minor >= versions.MINOR_56_BUILD_CONFIGDRIVE
+def allow_configdrive_vendor_data():
+ """Check if configdrive can contain a vendor_data key.
+
+ Version 1.59 of the API added support for configdrive vendor_data.
+ """
+ return (api.request.version.minor >=
+ versions.MINOR_59_CONFIGDRIVE_VENDOR_DATA)
+
+
def allow_allocation_update():
"""Check if updating an existing allocation is allowed or not.
diff --git a/ironic/api/controllers/v1/versions.py b/ironic/api/controllers/v1/versions.py
index cadd2b169..f4ab84618 100644
--- a/ironic/api/controllers/v1/versions.py
+++ b/ironic/api/controllers/v1/versions.py
@@ -96,6 +96,7 @@ BASE_VERSION = 1
# v1.56: Add support for building configdrives.
# v1.57: Add support for updating an exisiting allocation.
# v1.58: Add support for backfilling allocations.
+# v1.59: Add support vendor data in configdrives.
MINOR_0_JUNO = 0
MINOR_1_INITIAL_VERSION = 1
@@ -156,6 +157,7 @@ MINOR_55_DEPLOY_TEMPLATES = 55
MINOR_56_BUILD_CONFIGDRIVE = 56
MINOR_57_ALLOCATION_UPDATE = 57
MINOR_58_ALLOCATION_BACKFILL = 58
+MINOR_59_CONFIGDRIVE_VENDOR_DATA = 59
# When adding another version, update:
# - MINOR_MAX_VERSION
@@ -163,7 +165,7 @@ MINOR_58_ALLOCATION_BACKFILL = 58
# explanation of what changed in the new version
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
-MINOR_MAX_VERSION = MINOR_58_ALLOCATION_BACKFILL
+MINOR_MAX_VERSION = MINOR_59_CONFIGDRIVE_VENDOR_DATA
# String representations of the minor and maximum versions
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
diff --git a/ironic/common/release_mappings.py b/ironic/common/release_mappings.py
index 2f3e33e4c..695e3d897 100644
--- a/ironic/common/release_mappings.py
+++ b/ironic/common/release_mappings.py
@@ -197,7 +197,7 @@ RELEASE_MAPPING = {
}
},
'master': {
- 'api': '1.58',
+ 'api': '1.59',
'rpc': '1.48',
'objects': {
'Allocation': ['1.0'],
diff --git a/ironic/conductor/utils.py b/ironic/conductor/utils.py
index 1c6898c44..ebd75238f 100644
--- a/ironic/conductor/utils.py
+++ b/ironic/conductor/utils.py
@@ -831,7 +831,7 @@ def build_configdrive(node, configdrive):
:param node: an Ironic node object.
:param configdrive: A configdrive as a dict with keys ``meta_data``,
- ``network_data`` and ``user_data`` (all optional).
+ ``network_data``, ``user_data`` and ``vendor_data`` (all optional).
:returns: A gzipped and base64 encoded configdrive as a string.
"""
meta_data = configdrive.setdefault('meta_data', {})
@@ -847,7 +847,8 @@ def build_configdrive(node, configdrive):
LOG.debug('Building a configdrive for node %s', node.uuid)
return os_configdrive.build(meta_data, user_data=user_data,
- network_data=configdrive.get('network_data'))
+ network_data=configdrive.get('network_data'),
+ vendor_data=configdrive.get('vendor_data'))
def fast_track_able(task):
diff --git a/ironic/tests/unit/api/controllers/v1/test_node.py b/ironic/tests/unit/api/controllers/v1/test_node.py
index f5c5440a8..acd7c0df8 100644
--- a/ironic/tests/unit/api/controllers/v1/test_node.py
+++ b/ironic/tests/unit/api/controllers/v1/test_node.py
@@ -4162,11 +4162,12 @@ class TestPut(test_api_base.BaseApiTest):
def test_provision_with_deploy_configdrive_as_dict_all_fields(self):
fake_cd = {'user_data': {'serialize': 'me'},
'meta_data': {'hostname': 'example.com'},
- 'network_data': {'links': []}}
+ 'network_data': {'links': []},
+ 'vendor_data': {'foo': 'bar'}}
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
{'target': states.ACTIVE,
'configdrive': fake_cd},
- headers={api_base.Version.string: '1.56'})
+ headers={api_base.Version.string: '1.59'})
self.assertEqual(http_client.ACCEPTED, ret.status_code)
self.assertEqual(b'', ret.body)
self.mock_dnd.assert_called_once_with(context=mock.ANY,
diff --git a/ironic/tests/unit/api/controllers/v1/test_utils.py b/ironic/tests/unit/api/controllers/v1/test_utils.py
index d18fded4a..c1d06d1d7 100644
--- a/ironic/tests/unit/api/controllers/v1/test_utils.py
+++ b/ironic/tests/unit/api/controllers/v1/test_utils.py
@@ -479,6 +479,12 @@ class TestCheckAllowFields(base.TestCase):
mock_request.version.minor = 34
self.assertFalse(utils.allow_node_rebuild_with_configdrive())
+ def test_allow_configdrive_vendor_data(self, mock_request):
+ mock_request.version.minor = 59
+ self.assertTrue(utils.allow_configdrive_vendor_data())
+ mock_request.version.minor = 58
+ self.assertFalse(utils.allow_configdrive_vendor_data())
+
def test_check_allow_configdrive_fails(self, mock_request):
mock_request.version.minor = 35
self.assertRaises(wsme.exc.ClientSideError,
@@ -500,16 +506,27 @@ class TestCheckAllowFields(base.TestCase):
utils.check_allow_configdrive(states.ACTIVE, "abcd")
def test_check_allow_configdrive_as_dict(self, mock_request):
- mock_request.version.minor = 56
+ mock_request.version.minor = 59
utils.check_allow_configdrive(states.ACTIVE, {'meta_data': {}})
utils.check_allow_configdrive(states.ACTIVE, {'meta_data': {},
'network_data': {},
- 'user_data': {}})
+ 'user_data': {},
+ 'vendor_data': {}})
utils.check_allow_configdrive(states.ACTIVE, {'user_data': 'foo'})
utils.check_allow_configdrive(states.ACTIVE, {'user_data': ['foo']})
+ def test_check_allow_configdrive_vendor_data_failed(self, mock_request):
+ mock_request.version.minor = 58
+ self.assertRaises(wsme.exc.ClientSideError,
+ utils.check_allow_configdrive,
+ states.ACTIVE,
+ {'meta_data': {},
+ 'network_data': {},
+ 'user_data': {},
+ 'vendor_data': {}})
+
def test_check_allow_configdrive_as_dict_invalid(self, mock_request):
- mock_request.version.minor = 56
+ mock_request.version.minor = 59
self.assertRaises(wsme.exc.ClientSideError,
utils.check_allow_configdrive, states.REBUILD,
{'foo': 'bar'})
diff --git a/ironic/tests/unit/conductor/test_manager.py b/ironic/tests/unit/conductor/test_manager.py
index ac0b60716..de2bc5f72 100644
--- a/ironic/tests/unit/conductor/test_manager.py
+++ b/ironic/tests/unit/conductor/test_manager.py
@@ -2205,7 +2205,7 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
configdrive = 'foo'
self._test__do_node_deploy_ok(configdrive=configdrive)
- @mock.patch('openstack.baremetal.configdrive.build', autospec=True)
+ @mock.patch('openstack.baremetal.configdrive.build')
def test__do_node_deploy_configdrive_as_dict(self, mock_cd):
mock_cd.return_value = 'foo'
configdrive = {'user_data': 'abcd'}
@@ -2213,9 +2213,10 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
expected_configdrive='foo')
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
network_data=None,
- user_data=b'abcd')
+ user_data=b'abcd',
+ vendor_data=None)
- @mock.patch('openstack.baremetal.configdrive.build', autospec=True)
+ @mock.patch('openstack.baremetal.configdrive.build')
def test__do_node_deploy_configdrive_as_dict_with_meta_data(self, mock_cd):
mock_cd.return_value = 'foo'
configdrive = {'meta_data': {'uuid': uuidutils.generate_uuid(),
@@ -2225,9 +2226,10 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
expected_configdrive='foo')
mock_cd.assert_called_once_with(configdrive['meta_data'],
network_data=None,
- user_data=None)
+ user_data=None,
+ vendor_data=None)
- @mock.patch('openstack.baremetal.configdrive.build', autospec=True)
+ @mock.patch('openstack.baremetal.configdrive.build')
def test__do_node_deploy_configdrive_with_network_data(self, mock_cd):
mock_cd.return_value = 'foo'
configdrive = {'network_data': {'links': []}}
@@ -2235,9 +2237,10 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
expected_configdrive='foo')
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
network_data={'links': []},
- user_data=None)
+ user_data=None,
+ vendor_data=None)
- @mock.patch('openstack.baremetal.configdrive.build', autospec=True)
+ @mock.patch('openstack.baremetal.configdrive.build')
def test__do_node_deploy_configdrive_and_user_data_as_dict(self, mock_cd):
mock_cd.return_value = 'foo'
configdrive = {'user_data': {'user': 'data'}}
@@ -2245,7 +2248,19 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
expected_configdrive='foo')
mock_cd.assert_called_once_with({'uuid': self.node.uuid},
network_data=None,
- user_data=b'{"user": "data"}')
+ user_data=b'{"user": "data"}',
+ vendor_data=None)
+
+ @mock.patch('openstack.baremetal.configdrive.build')
+ def test__do_node_deploy_configdrive_with_vendor_data(self, mock_cd):
+ mock_cd.return_value = 'foo'
+ configdrive = {'vendor_data': {'foo': 'bar'}}
+ self._test__do_node_deploy_ok(configdrive=configdrive,
+ expected_configdrive='foo')
+ mock_cd.assert_called_once_with({'uuid': self.node.uuid},
+ network_data=None,
+ user_data=None,
+ vendor_data={'foo': 'bar'})
@mock.patch.object(swift, 'SwiftAPI')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.prepare')
diff --git a/lower-constraints.txt b/lower-constraints.txt
index de22850c3..8d1909027 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -20,7 +20,7 @@ keystoneauth1==3.15.0
keystonemiddleware==4.17.0
mock==3.0.0
openstackdocstheme==1.20.0
-openstacksdk==0.31.2
+openstacksdk==0.37.0
os-api-ref==1.4.0
os-traits==0.4.0
oslo.concurrency==3.26.0
diff --git a/releasenotes/notes/configdrive-vendordata-122049bd7c6e1b67.yaml b/releasenotes/notes/configdrive-vendordata-122049bd7c6e1b67.yaml
new file mode 100644
index 000000000..4334e3a8f
--- /dev/null
+++ b/releasenotes/notes/configdrive-vendordata-122049bd7c6e1b67.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Adds support for specifying vendor_data when building config drives.
+ Starting with API version 1.59, a JSON based ``configdrive`` parameter to
+ ``/v1/nodes/<node>/states/provision`` can include the key vendor_data.
+ This data will be built into the configdrive contents as
+ vendor_data2.json. \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index d57e6ea7c..76ba55b92 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -47,4 +47,4 @@ jsonschema>=2.6.0 # MIT
psutil>=3.2.2 # BSD
futurist>=1.2.0 # Apache-2.0
tooz>=1.58.0 # Apache-2.0
-openstacksdk>=0.31.2 # Apache-2.0
+openstacksdk>=0.37.0 # Apache-2.0