Support passing a dictionary for configdrive
API version 1.56 introduces support for building configdrive on the server side. In this case configdrive is a dictionary, this changes adds support for it. Change-Id: I2b870fc89aa31c13b33f76b14acd6ee959800554 Depends-On: https://review.openstack.org/639050 Story: #2005083 Task: #29841
This commit is contained in:
parent
cc37253428
commit
595db83ddc
|
@ -43,7 +43,7 @@ from ironicclient import exc
|
|||
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
|
||||
# for full details.
|
||||
DEFAULT_VER = '1.9'
|
||||
LAST_KNOWN_API_VERSION = 55
|
||||
LAST_KNOWN_API_VERSION = 56
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import argparse
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
|
@ -30,8 +31,10 @@ from ironicclient.v1 import utils as v1_utils
|
|||
CONFIG_DRIVE_ARG_HELP = _(
|
||||
"A gzipped, base64-encoded configuration drive string OR "
|
||||
"the path to the configuration drive file OR the path to a "
|
||||
"directory containing the config drive files. In case it's "
|
||||
"a directory, a config drive will be generated from it.")
|
||||
"directory containing the config drive files OR a JSON object to build "
|
||||
"config drive from. In case it's a directory, a config drive will be "
|
||||
"generated from it. In case it's a JSON object, a config drive will "
|
||||
"be generated on the server side.")
|
||||
|
||||
|
||||
SUPPORTED_INTERFACES = ['bios', 'boot', 'console', 'deploy', 'inspect',
|
||||
|
@ -69,6 +72,15 @@ class ProvisionStateBaremetalNode(command.Command):
|
|||
clean_steps = utils.handle_json_arg(clean_steps, 'clean steps')
|
||||
|
||||
config_drive = getattr(parsed_args, 'config_drive', None)
|
||||
if config_drive:
|
||||
try:
|
||||
config_drive_dict = json.loads(config_drive)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(config_drive_dict, dict):
|
||||
config_drive = config_drive_dict
|
||||
|
||||
rescue_password = getattr(parsed_args, 'rescue_password', None)
|
||||
|
||||
baremetal_client.node.set_provision_state(
|
||||
|
|
|
@ -1390,6 +1390,25 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
|
|||
'node_uuid', 'active',
|
||||
cleansteps=None, configdrive='path/to/drive', rescue_password=None)
|
||||
|
||||
def test_deploy_baremetal_provision_state_active_and_configdrive_dict(
|
||||
self):
|
||||
arglist = ['node_uuid',
|
||||
'--config-drive', '{"meta_data": {}}']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'active'),
|
||||
('config_drive', '{"meta_data": {}}'),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'active',
|
||||
cleansteps=None, configdrive={'meta_data': {}},
|
||||
rescue_password=None)
|
||||
|
||||
def test_deploy_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [
|
||||
|
|
|
@ -1388,6 +1388,16 @@ class NodeManagerTest(testtools.TestCase):
|
|||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
|
||||
def test_node_set_provision_state_with_configdrive_as_dict(self):
|
||||
target_state = 'active'
|
||||
self.mgr.set_provision_state(NODE1['uuid'], target_state,
|
||||
configdrive={'user_data': ''})
|
||||
body = {'target': target_state, 'configdrive': {'user_data': ''}}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/provision' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
|
||||
def test_node_set_provision_state_with_configdrive_file(self):
|
||||
target_state = 'active'
|
||||
file_content = b'foo bar cat meow dog bark'
|
||||
|
|
|
@ -515,9 +515,11 @@ class NodeManager(base.CreateManager):
|
|||
'rescue', 'unrescue'.
|
||||
:param configdrive: A gzipped, base64-encoded configuration drive
|
||||
string OR the path to the configuration drive file OR the path to
|
||||
a directory containing the config drive files. In case it's a
|
||||
directory, a config drive will be generated from it. This is only
|
||||
valid when setting state to 'active'.
|
||||
a directory containing the config drive files OR a dictionary to
|
||||
build config drive from. In case it's a directory, a config drive
|
||||
will be generated from it. In case it's a dictionary, a config
|
||||
drive will be generated on the server side (requires API version
|
||||
1.56). This is only valid when setting state to 'active'.
|
||||
:param cleansteps: The clean steps as a list of clean-step
|
||||
dictionaries; each dictionary should have keys 'interface' and
|
||||
'step', and optional key 'args'. This must be specified (and is
|
||||
|
@ -534,11 +536,12 @@ class NodeManager(base.CreateManager):
|
|||
path = "%s/states/provision" % node_uuid
|
||||
body = {'target': state}
|
||||
if configdrive:
|
||||
if os.path.isfile(configdrive):
|
||||
with open(configdrive, 'rb') as f:
|
||||
configdrive = f.read()
|
||||
if os.path.isdir(configdrive):
|
||||
configdrive = utils.make_configdrive(configdrive)
|
||||
if not isinstance(configdrive, dict):
|
||||
if os.path.isfile(configdrive):
|
||||
with open(configdrive, 'rb') as f:
|
||||
configdrive = f.read()
|
||||
if os.path.isdir(configdrive):
|
||||
configdrive = utils.make_configdrive(configdrive)
|
||||
|
||||
body['configdrive'] = configdrive
|
||||
elif cleansteps:
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Supports passing a JSON object to ``--config-drive`` to build the config
|
||||
drive on the server side (requires API version 1.56).
|
Loading…
Reference in New Issue