Merge "Completely remove support for setting IPMI credentials"
This commit is contained in:
commit
095b4e3d72
@ -15,11 +15,6 @@ done prior to calling the endpoint.
|
||||
|
||||
Requires X-Auth-Token header with Keystone token for authentication.
|
||||
|
||||
Deprecated parameters (only available in API before version ``1.9``):
|
||||
|
||||
* ``new_ipmi_password``
|
||||
* ``new_ipmi_username``
|
||||
|
||||
Response:
|
||||
|
||||
* 202 - accepted introspection request
|
||||
@ -289,12 +284,7 @@ Response:
|
||||
* 403 - node is not on introspection
|
||||
* 404 - node cannot be found or multiple nodes found
|
||||
|
||||
Response body: JSON dictionary. If setting IPMI credentials (deprecated
|
||||
feature) is requested, body will contain the following keys:
|
||||
|
||||
* ``ipmi_setup_credentials`` boolean ``True``
|
||||
* ``ipmi_username`` new IPMI user name
|
||||
* ``ipmi_password`` new IPMI password
|
||||
Response body: JSON dictionary with ``uuid`` key.
|
||||
|
||||
.. _hardware inventory: http://docs.openstack.org/developer/ironic-python-agent/#hardware-inventory
|
||||
.. _Specifying the disk for deployment root device hints:
|
||||
@ -398,3 +388,5 @@ Version History
|
||||
* **1.10** adds node state to the GET /v1/introspection/<Node ID> and
|
||||
GET /v1/introspection API response data.
|
||||
* **1.11** adds invert&multiple fields into rules response data
|
||||
* **1.12** this version indicates that support for setting IPMI credentials
|
||||
was completely removed from API (all versions).
|
||||
|
@ -320,7 +320,6 @@ These steps are avoided, based on the feature requirements:
|
||||
|
||||
Limitations:
|
||||
|
||||
* IPMI credentials are not updated --- ramdisk not running
|
||||
* there's no way to update the unprocessed data atm.
|
||||
* the unprocessed data is never cleaned from the store
|
||||
* check for stored data presence is performed in background;
|
||||
|
@ -27,7 +27,6 @@ LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
# See http://specs.openstack.org/openstack/ironic-specs/specs/kilo/new-ironic-state-machine.html # noqa
|
||||
VALID_STATES = {'enroll', 'manageable', 'inspecting', 'inspect failed'}
|
||||
SET_CREDENTIALS_VALID_STATES = {'enroll'}
|
||||
|
||||
# 1.19 is API version, which supports port.pxe_enabled
|
||||
DEFAULT_IRONIC_API_VERSION = '1.19'
|
||||
@ -143,15 +142,9 @@ def get_client(token=None,
|
||||
return client.Client(1, **args)
|
||||
|
||||
|
||||
def check_provision_state(node, with_credentials=False):
|
||||
def check_provision_state(node):
|
||||
state = node.provision_state.lower()
|
||||
if with_credentials and state not in SET_CREDENTIALS_VALID_STATES:
|
||||
msg = _('Invalid provision state for setting IPMI credentials: '
|
||||
'"%(state)s", valid states are %(valid)s')
|
||||
raise utils.Error(msg % {'state': state,
|
||||
'valid': list(SET_CREDENTIALS_VALID_STATES)},
|
||||
node_info=node)
|
||||
elif not with_credentials and state not in VALID_STATES:
|
||||
if state not in VALID_STATES:
|
||||
msg = _('Invalid provision state for introspection: '
|
||||
'"%(state)s", valid states are "%(valid)s"')
|
||||
raise utils.Error(msg % {'state': state, 'valid': list(VALID_STATES)},
|
||||
|
@ -76,12 +76,6 @@ PROCESSING_OPTS = [
|
||||
help=_('Whether to overwrite existing values in node '
|
||||
'database. Disable this option to make '
|
||||
'introspection a non-destructive operation.')),
|
||||
cfg.BoolOpt('enable_setting_ipmi_credentials',
|
||||
default=False,
|
||||
help=_('Whether to enable setting IPMI credentials during '
|
||||
'introspection. This feature will be removed in the '
|
||||
'Pike release.'),
|
||||
deprecated_for_removal=True),
|
||||
cfg.StrOpt('default_processing_hooks',
|
||||
default='ramdisk_error,root_disk_selection,scheduler,'
|
||||
'validate_interfaces,capabilities,pci_devices',
|
||||
|
@ -14,7 +14,6 @@
|
||||
"""Handling introspection request."""
|
||||
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
|
||||
from eventlet import semaphore
|
||||
@ -31,67 +30,32 @@ CONF = cfg.CONF
|
||||
|
||||
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
PASSWORD_ACCEPTED_CHARS = set(string.ascii_letters + string.digits)
|
||||
PASSWORD_MAX_LENGTH = 20 # IPMI v2.0
|
||||
|
||||
_LAST_INTROSPECTION_TIME = 0
|
||||
_LAST_INTROSPECTION_LOCK = semaphore.BoundedSemaphore()
|
||||
|
||||
|
||||
def _validate_ipmi_credentials(node, new_ipmi_credentials):
|
||||
if not CONF.processing.enable_setting_ipmi_credentials:
|
||||
raise utils.Error(
|
||||
_('IPMI credentials setup is disabled in configuration'))
|
||||
|
||||
new_username, new_password = new_ipmi_credentials
|
||||
if not new_username:
|
||||
new_username = node.driver_info.get('ipmi_username')
|
||||
if not new_username:
|
||||
raise utils.Error(_('Setting IPMI credentials requested, but neither '
|
||||
'new user name nor driver_info[ipmi_username] '
|
||||
'are provided'),
|
||||
node_info=node)
|
||||
wrong_chars = {c for c in new_password
|
||||
if c not in PASSWORD_ACCEPTED_CHARS}
|
||||
if wrong_chars:
|
||||
raise utils.Error(_('Forbidden characters encountered in new IPMI '
|
||||
'password: "%s"; use only letters and numbers')
|
||||
% ''.join(wrong_chars), node_info=node)
|
||||
if not 0 < len(new_password) <= PASSWORD_MAX_LENGTH:
|
||||
raise utils.Error(_('IPMI password length should be > 0 and <= %d')
|
||||
% PASSWORD_MAX_LENGTH, node_info=node)
|
||||
|
||||
return new_username, new_password
|
||||
|
||||
|
||||
def introspect(node_id, new_ipmi_credentials=None, token=None):
|
||||
def introspect(node_id, token=None):
|
||||
"""Initiate hardware properties introspection for a given node.
|
||||
|
||||
:param node_id: node UUID or name
|
||||
:param new_ipmi_credentials: tuple (new username, new password) or None
|
||||
:param token: authentication token
|
||||
:raises: Error
|
||||
"""
|
||||
ironic = ir_utils.get_client(token)
|
||||
node = ir_utils.get_node(node_id, ironic=ironic)
|
||||
|
||||
ir_utils.check_provision_state(node, with_credentials=new_ipmi_credentials)
|
||||
|
||||
if new_ipmi_credentials:
|
||||
new_ipmi_credentials = (
|
||||
_validate_ipmi_credentials(node, new_ipmi_credentials))
|
||||
else:
|
||||
validation = ironic.node.validate(node.uuid)
|
||||
if not validation.power['result']:
|
||||
msg = _('Failed validation of power interface, reason: %s')
|
||||
raise utils.Error(msg % validation.power['reason'],
|
||||
node_info=node)
|
||||
ir_utils.check_provision_state(node)
|
||||
validation = ironic.node.validate(node.uuid)
|
||||
if not validation.power['result']:
|
||||
msg = _('Failed validation of power interface, reason: %s')
|
||||
raise utils.Error(msg % validation.power['reason'],
|
||||
node_info=node)
|
||||
|
||||
bmc_address = ir_utils.get_ipmi_address(node)
|
||||
node_info = node_cache.start_introspection(node.uuid,
|
||||
bmc_address=bmc_address,
|
||||
ironic=ironic)
|
||||
node_info.set_option('new_ipmi_credentials', new_ipmi_credentials)
|
||||
|
||||
def _handle_exceptions(fut):
|
||||
try:
|
||||
@ -111,17 +75,16 @@ def introspect(node_id, new_ipmi_credentials=None, token=None):
|
||||
def _background_introspect(ironic, node_info):
|
||||
global _LAST_INTROSPECTION_TIME
|
||||
|
||||
if not node_info.options.get('new_ipmi_credentials'):
|
||||
if re.match(CONF.introspection_delay_drivers, node_info.node().driver):
|
||||
LOG.debug('Attempting to acquire lock on last introspection time')
|
||||
with _LAST_INTROSPECTION_LOCK:
|
||||
delay = (_LAST_INTROSPECTION_TIME - time.time()
|
||||
+ CONF.introspection_delay)
|
||||
if delay > 0:
|
||||
LOG.debug('Waiting %d seconds before sending the next '
|
||||
'node on introspection', delay)
|
||||
time.sleep(delay)
|
||||
_LAST_INTROSPECTION_TIME = time.time()
|
||||
if re.match(CONF.introspection_delay_drivers, node_info.node().driver):
|
||||
LOG.debug('Attempting to acquire lock on last introspection time')
|
||||
with _LAST_INTROSPECTION_LOCK:
|
||||
delay = (_LAST_INTROSPECTION_TIME - time.time()
|
||||
+ CONF.introspection_delay)
|
||||
if delay > 0:
|
||||
LOG.debug('Waiting %d seconds before sending the next '
|
||||
'node on introspection', delay)
|
||||
time.sleep(delay)
|
||||
_LAST_INTROSPECTION_TIME = time.time()
|
||||
|
||||
node_info.acquire_lock()
|
||||
try:
|
||||
@ -151,26 +114,21 @@ def _background_introspect_locked(node_info, ironic):
|
||||
LOG.info('The following attributes will be used for look up: %s',
|
||||
attrs, node_info=node_info)
|
||||
|
||||
if not node_info.options.get('new_ipmi_credentials'):
|
||||
try:
|
||||
ironic.node.set_boot_device(node_info.uuid, 'pxe',
|
||||
persistent=False)
|
||||
except Exception as exc:
|
||||
LOG.warning('Failed to set boot device to PXE: %s',
|
||||
exc, node_info=node_info)
|
||||
try:
|
||||
ironic.node.set_boot_device(node_info.uuid, 'pxe',
|
||||
persistent=False)
|
||||
except Exception as exc:
|
||||
LOG.warning('Failed to set boot device to PXE: %s',
|
||||
exc, node_info=node_info)
|
||||
|
||||
try:
|
||||
ironic.node.set_power_state(node_info.uuid, 'reboot')
|
||||
except Exception as exc:
|
||||
raise utils.Error(_('Failed to power on the node, check it\'s '
|
||||
'power management configuration: %s'),
|
||||
exc, node_info=node_info)
|
||||
LOG.info('Introspection started successfully',
|
||||
node_info=node_info)
|
||||
else:
|
||||
LOG.info('Introspection environment is ready, manual power on is '
|
||||
'required within %d seconds', CONF.timeout,
|
||||
node_info=node_info)
|
||||
try:
|
||||
ironic.node.set_power_state(node_info.uuid, 'reboot')
|
||||
except Exception as exc:
|
||||
raise utils.Error(_('Failed to power on the node, check it\'s '
|
||||
'power management configuration: %s'),
|
||||
exc, node_info=node_info)
|
||||
LOG.info('Introspection started successfully',
|
||||
node_info=node_info)
|
||||
|
||||
|
||||
def abort(node_id, token=None):
|
||||
|
@ -38,10 +38,8 @@ app = flask.Flask(__name__)
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
MINIMUM_API_VERSION = (1, 0)
|
||||
# TODO(dtantsur): set to the current version as soon we move setting IPMI
|
||||
# credentials support completely.
|
||||
DEFAULT_API_VERSION = (1, 8)
|
||||
CURRENT_API_VERSION = (1, 11)
|
||||
CURRENT_API_VERSION = (1, 12)
|
||||
DEFAULT_API_VERSION = CURRENT_API_VERSION
|
||||
_LOGGING_EXCLUDED_KEYS = ('logs',)
|
||||
|
||||
|
||||
@ -204,23 +202,7 @@ def api_introspection(node_id):
|
||||
utils.check_auth(flask.request)
|
||||
|
||||
if flask.request.method == 'POST':
|
||||
new_ipmi_password = flask.request.args.get('new_ipmi_password',
|
||||
type=str,
|
||||
default=None)
|
||||
if new_ipmi_password:
|
||||
new_ipmi_username = flask.request.args.get('new_ipmi_username',
|
||||
type=str,
|
||||
default=None)
|
||||
new_ipmi_credentials = (new_ipmi_username, new_ipmi_password)
|
||||
else:
|
||||
new_ipmi_credentials = None
|
||||
|
||||
if new_ipmi_credentials and _get_version() >= (1, 9):
|
||||
return _('Setting IPMI credentials is deprecated and not allowed '
|
||||
'starting with API version 1.9'), 400
|
||||
|
||||
introspect.introspect(node_id,
|
||||
new_ipmi_credentials=new_ipmi_credentials,
|
||||
token=flask.request.headers.get('X-Auth-Token'))
|
||||
return '', 202
|
||||
else:
|
||||
|
@ -18,7 +18,6 @@ import datetime
|
||||
import json
|
||||
import os
|
||||
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import base64
|
||||
from oslo_utils import excutils
|
||||
@ -38,8 +37,6 @@ CONF = cfg.CONF
|
||||
|
||||
LOG = utils.getProcessingLogger(__name__)
|
||||
|
||||
_CREDENTIALS_WAIT_RETRIES = 10
|
||||
_CREDENTIALS_WAIT_PERIOD = 3
|
||||
_STORAGE_EXCLUDED_KEYS = {'logs'}
|
||||
_UNPROCESSED_DATA_STORE_SUFFIX = 'UNPROCESSED'
|
||||
|
||||
@ -279,56 +276,12 @@ def _process_node(node_info, node, introspection_data):
|
||||
|
||||
resp = {'uuid': node.uuid}
|
||||
|
||||
if node_info.options.get('new_ipmi_credentials'):
|
||||
new_username, new_password = (
|
||||
node_info.options.get('new_ipmi_credentials'))
|
||||
utils.executor().submit(_finish_set_ipmi_credentials,
|
||||
node_info, ironic, node, introspection_data,
|
||||
new_username, new_password)
|
||||
resp['ipmi_setup_credentials'] = True
|
||||
resp['ipmi_username'] = new_username
|
||||
resp['ipmi_password'] = new_password
|
||||
else:
|
||||
utils.executor().submit(_finish, node_info, ironic, introspection_data,
|
||||
power_off=CONF.processing.power_off)
|
||||
utils.executor().submit(_finish, node_info, ironic, introspection_data,
|
||||
power_off=CONF.processing.power_off)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@node_cache.fsm_transition(istate.Events.finish)
|
||||
def _finish_set_ipmi_credentials(node_info, ironic, node, introspection_data,
|
||||
new_username, new_password):
|
||||
patch = [{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': new_username},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': new_password}]
|
||||
new_ipmi_address = utils.get_ipmi_address_from_data(introspection_data)
|
||||
if not ir_utils.get_ipmi_address(node) and new_ipmi_address:
|
||||
patch.append({'op': 'add', 'path': '/driver_info/ipmi_address',
|
||||
'value': new_ipmi_address})
|
||||
node_info.patch(patch)
|
||||
|
||||
for attempt in range(_CREDENTIALS_WAIT_RETRIES):
|
||||
try:
|
||||
# We use this call because it requires valid credentials.
|
||||
# We don't care about boot device, obviously.
|
||||
ironic.node.get_boot_device(node_info.uuid)
|
||||
except Exception as exc:
|
||||
LOG.info('Waiting for credentials update, attempt %(attempt)d '
|
||||
'current error is %(exc)s',
|
||||
{'attempt': attempt, 'exc': exc},
|
||||
node_info=node_info, data=introspection_data)
|
||||
eventlet.greenthread.sleep(_CREDENTIALS_WAIT_PERIOD)
|
||||
else:
|
||||
_finish_common(node_info, ironic, introspection_data)
|
||||
return
|
||||
|
||||
msg = (_('Failed to validate updated IPMI credentials for node '
|
||||
'%s, node might require maintenance') % node_info.uuid)
|
||||
node_info.finished(error=msg)
|
||||
raise utils.Error(msg, node_info=node_info, data=introspection_data)
|
||||
|
||||
|
||||
def _finish_common(node_info, ironic, introspection_data, power_off=True):
|
||||
if power_off:
|
||||
LOG.debug('Forcing power off of node %s', node_info.uuid)
|
||||
|
@ -55,8 +55,6 @@ os_password = password
|
||||
os_tenant_name = tenant
|
||||
[firewall]
|
||||
manage_firewall = False
|
||||
[processing]
|
||||
enable_setting_ipmi_credentials = True
|
||||
[DEFAULT]
|
||||
debug = True
|
||||
auth_strategy = noauth
|
||||
@ -164,13 +162,8 @@ class Base(base.NodeTest):
|
||||
raise AssertionError(msg)
|
||||
return res
|
||||
|
||||
def call_introspect(self, uuid, new_ipmi_username=None,
|
||||
new_ipmi_password=None, **kwargs):
|
||||
def call_introspect(self, uuid, **kwargs):
|
||||
endpoint = '/v1/introspection/%s' % uuid
|
||||
if new_ipmi_password:
|
||||
endpoint += '?new_ipmi_password=%s' % new_ipmi_password
|
||||
if new_ipmi_username:
|
||||
endpoint += '&new_ipmi_username=%s' % new_ipmi_username
|
||||
return self.call('post', endpoint, **kwargs)
|
||||
|
||||
def call_get_status(self, uuid, **kwargs):
|
||||
@ -310,37 +303,6 @@ class Test(Base):
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=True, state=istate.States.finished)
|
||||
|
||||
def test_setup_ipmi(self):
|
||||
patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': 'admin'},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': 'pwd'},
|
||||
]
|
||||
self.node.provision_state = 'enroll'
|
||||
self.call_introspect(self.uuid, new_ipmi_username='admin',
|
||||
new_ipmi_password='pwd')
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
self.assertFalse(self.cli.node.set_power_state.called)
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=False, state=istate.States.waiting)
|
||||
|
||||
res = self.call_continue(self.data)
|
||||
self.assertEqual('admin', res['ipmi_username'])
|
||||
self.assertEqual('pwd', res['ipmi_password'])
|
||||
self.assertTrue(res['ipmi_setup_credentials'])
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
|
||||
self.assertCalledWithPatch(self.patch + patch_credentials,
|
||||
self.cli.node.update)
|
||||
self.cli.port.create.assert_called_once_with(
|
||||
node_uuid=self.uuid, address='11:22:33:44:55:66', extra={},
|
||||
pxe_enabled=True)
|
||||
|
||||
status = self.call_get_status(self.uuid)
|
||||
self.check_status(status, finished=True, state=istate.States.finished)
|
||||
|
||||
def test_introspection_statuses(self):
|
||||
self.call_introspect(self.uuid)
|
||||
eventlet.greenthread.sleep(DEFAULT_SLEEP)
|
||||
|
@ -72,8 +72,6 @@ class TestIntrospect(BaseTest):
|
||||
persistent=False)
|
||||
cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||
'reboot')
|
||||
self.node_info.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', None)
|
||||
self.node_info.acquire_lock.assert_called_once_with()
|
||||
self.node_info.release_lock.assert_called_once_with()
|
||||
|
||||
@ -99,8 +97,6 @@ class TestIntrospect(BaseTest):
|
||||
persistent=False)
|
||||
cli.node.set_power_state.assert_called_once_with(self.uuid,
|
||||
'reboot')
|
||||
self.node_info.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', None)
|
||||
self.node_info.acquire_lock.assert_called_once_with()
|
||||
self.node_info.release_lock.assert_called_once_with()
|
||||
|
||||
@ -332,89 +328,6 @@ class TestIntrospect(BaseTest):
|
||||
self.assertEqual(42, introspect._LAST_INTROSPECTION_TIME)
|
||||
|
||||
|
||||
@mock.patch.object(firewall, 'update_filters', autospec=True)
|
||||
@mock.patch.object(node_cache, 'start_introspection', autospec=True)
|
||||
@mock.patch.object(ir_utils, 'get_client', autospec=True)
|
||||
class TestSetIpmiCredentials(BaseTest):
|
||||
def setUp(self):
|
||||
super(TestSetIpmiCredentials, self).setUp()
|
||||
CONF.set_override('enable_setting_ipmi_credentials', True,
|
||||
'processing')
|
||||
self.new_creds = ('user', 'password')
|
||||
self.node_info.options['new_ipmi_credentials'] = self.new_creds
|
||||
self.node.provision_state = 'enroll'
|
||||
|
||||
def test_ok(self, client_mock, start_mock, filters_mock):
|
||||
cli = self._prepare(client_mock)
|
||||
start_mock.return_value = self.node_info
|
||||
|
||||
introspect.introspect(self.uuid, new_ipmi_credentials=self.new_creds)
|
||||
|
||||
start_mock.assert_called_once_with(self.uuid,
|
||||
bmc_address=self.bmc_address,
|
||||
ironic=cli)
|
||||
filters_mock.assert_called_with(cli)
|
||||
self.assertFalse(cli.node.validate.called)
|
||||
self.assertFalse(cli.node.set_boot_device.called)
|
||||
self.assertFalse(cli.node.set_power_state.called)
|
||||
start_mock.return_value.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', self.new_creds)
|
||||
|
||||
def test_disabled(self, client_mock, start_mock, filters_mock):
|
||||
CONF.set_override('enable_setting_ipmi_credentials', False,
|
||||
'processing')
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaisesRegex(utils.Error, 'disabled',
|
||||
introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_no_username(self, client_mock, start_mock, filters_mock):
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=(None, 'password'))
|
||||
|
||||
def test_default_username(self, client_mock, start_mock, filters_mock):
|
||||
cli = self._prepare(client_mock)
|
||||
start_mock.return_value = self.node_info
|
||||
self.node.driver_info['ipmi_username'] = self.new_creds[0]
|
||||
|
||||
introspect.introspect(self.uuid,
|
||||
new_ipmi_credentials=(None, self.new_creds[1]))
|
||||
|
||||
start_mock.assert_called_once_with(self.uuid,
|
||||
bmc_address=self.bmc_address,
|
||||
ironic=cli)
|
||||
filters_mock.assert_called_with(cli)
|
||||
self.assertFalse(cli.node.validate.called)
|
||||
self.assertFalse(cli.node.set_boot_device.called)
|
||||
self.assertFalse(cli.node.set_power_state.called)
|
||||
start_mock.return_value.set_option.assert_called_once_with(
|
||||
'new_ipmi_credentials', self.new_creds)
|
||||
|
||||
def test_wrong_letters(self, client_mock, start_mock, filters_mock):
|
||||
self.new_creds = ('user', 'p ssw@rd')
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_too_long(self, client_mock, start_mock, filters_mock):
|
||||
self.new_creds = ('user', 'password' * 100)
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
def test_wrong_state(self, client_mock, start_mock, filters_mock):
|
||||
self.node.provision_state = 'manageable'
|
||||
self._prepare(client_mock)
|
||||
|
||||
self.assertRaises(utils.Error, introspect.introspect, self.uuid,
|
||||
new_ipmi_credentials=self.new_creds)
|
||||
|
||||
|
||||
@mock.patch.object(firewall, 'update_filters', autospec=True)
|
||||
@mock.patch.object(node_cache, 'get_node', autospec=True)
|
||||
@mock.patch.object(ir_utils, 'get_client', autospec=True)
|
||||
|
@ -55,38 +55,8 @@ class TestApiIntrospect(BaseAPITest):
|
||||
res = self.app.post('/v1/introspection/%s' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(self.uuid,
|
||||
new_ipmi_credentials=None,
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials(self, introspect_mock):
|
||||
res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&'
|
||||
'new_ipmi_password=password' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=('user', 'password'),
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials_disabled(self, introspect_mock):
|
||||
headers = {conf.VERSION_HEADER: '1.9'}
|
||||
res = self.app.post('/v1/introspection/%s?new_ipmi_username=user&'
|
||||
'new_ipmi_password=password' % self.uuid,
|
||||
headers=headers)
|
||||
self.assertEqual(400, res.status_code)
|
||||
self.assertFalse(introspect_mock.called)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_introspect_set_ipmi_credentials_no_user(self, introspect_mock):
|
||||
res = self.app.post('/v1/introspection/%s?'
|
||||
'new_ipmi_password=password' % self.uuid)
|
||||
self.assertEqual(202, res.status_code)
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=(None, 'password'),
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(introspect, 'introspect', autospec=True)
|
||||
def test_intospect_failed(self, introspect_mock):
|
||||
introspect_mock.side_effect = utils.Error("boom")
|
||||
@ -97,7 +67,6 @@ class TestApiIntrospect(BaseAPITest):
|
||||
json.loads(res.data.decode('utf-8'))['error']['message'])
|
||||
introspect_mock.assert_called_once_with(
|
||||
self.uuid,
|
||||
new_ipmi_credentials=None,
|
||||
token=None)
|
||||
|
||||
@mock.patch.object(utils, 'check_auth', autospec=True)
|
||||
|
@ -353,14 +353,6 @@ class TestProcessNode(BaseTest):
|
||||
self.data['interfaces'] = self.valid_interfaces
|
||||
self.ports = self.all_ports
|
||||
|
||||
self.new_creds = ('user', 'password')
|
||||
self.patch_credentials = [
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_username',
|
||||
'value': self.new_creds[0]},
|
||||
{'op': 'add', 'path': '/driver_info/ipmi_password',
|
||||
'value': self.new_creds[1]},
|
||||
]
|
||||
|
||||
self.cli.node.get_boot_device.side_effect = (
|
||||
[RuntimeError()] * self.validate_attempts + [None])
|
||||
self.cli.port.create.side_effect = self.ports
|
||||
@ -382,12 +374,6 @@ class TestProcessNode(BaseTest):
|
||||
ret_val = process._process_node(self.node_info, self.node, self.data)
|
||||
self.assertEqual(self.uuid, ret_val.get('uuid'))
|
||||
|
||||
def test_return_includes_uuid_with_ipmi_creds(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
ret_val = process._process_node(self.node_info, self.node, self.data)
|
||||
self.assertEqual(self.uuid, ret_val.get('uuid'))
|
||||
self.assertTrue(ret_val.get('ipmi_setup_credentials'))
|
||||
|
||||
@mock.patch.object(example_plugin.ExampleProcessingHook, 'before_update')
|
||||
def test_wrong_provision_state(self, post_hook_mock):
|
||||
self.node.provision_state = 'active'
|
||||
@ -428,49 +414,6 @@ class TestProcessNode(BaseTest):
|
||||
address=self.macs[1],
|
||||
extra={}, pxe_enabled=False)
|
||||
|
||||
def test_set_ipmi_credentials(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.cli.node.get_boot_device.assert_called_with(self.uuid)
|
||||
self.assertEqual(self.validate_attempts + 1,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
|
||||
def test_set_ipmi_credentials_no_address(self):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
del self.node.driver_info['ipmi_address']
|
||||
self.patch_credentials.append({'op': 'add',
|
||||
'path': '/driver_info/ipmi_address',
|
||||
'value': self.bmc_address})
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.cli.node.set_power_state.assert_called_once_with(self.uuid, 'off')
|
||||
self.cli.node.get_boot_device.assert_called_with(self.uuid)
|
||||
self.assertEqual(self.validate_attempts + 1,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'finished', autospec=True)
|
||||
def test_set_ipmi_credentials_timeout(self, finished_mock):
|
||||
self.node_info.set_option('new_ipmi_credentials', self.new_creds)
|
||||
self.cli.node.get_boot_device.side_effect = RuntimeError('boom')
|
||||
|
||||
process._process_node(self.node_info, self.node, self.data)
|
||||
|
||||
self.cli.node.update.assert_any_call(self.uuid, self.patch_credentials)
|
||||
self.assertEqual(2, self.cli.node.update.call_count)
|
||||
self.assertEqual(process._CREDENTIALS_WAIT_RETRIES,
|
||||
self.cli.node.get_boot_device.call_count)
|
||||
self.assertFalse(self.cli.node.set_power_state.called)
|
||||
finished_mock.assert_called_once_with(
|
||||
mock.ANY,
|
||||
error='Failed to validate updated IPMI credentials for node %s, '
|
||||
'node might require maintenance' % self.uuid)
|
||||
|
||||
@mock.patch.object(node_cache.NodeInfo, 'finished', autospec=True)
|
||||
def test_power_off_failed(self, finished_mock):
|
||||
self.cli.node.set_power_state.side_effect = RuntimeError('boom')
|
||||
@ -609,7 +552,6 @@ class TestReapplyNode(BaseTest):
|
||||
started_at=self.started_at,
|
||||
node=self.node)
|
||||
self.node_info.invalidate_cache = mock.Mock()
|
||||
self.new_creds = ('user', 'password')
|
||||
|
||||
self.cli.port.create.side_effect = self.ports
|
||||
self.cli.node.update.return_value = self.node
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Experimental setting IPMI credentials support was removed from all versions
|
||||
of the API. The current API version was bumped to 1.12 to mark this change.
|
||||
- |
|
||||
The default API version was synchronized with the current API version again
|
||||
after removal of the IPMI credentials setting.
|
Loading…
x
Reference in New Issue
Block a user