summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Bogun <dbogun@mirantis.com>2017-02-16 19:05:47 +0200
committerDmitry Bogun <dbogun@mirantis.com>2017-02-28 16:06:57 +0200
commit74682642008bfc45d1c7a9bd583e6df8e83e8029 (patch)
tree6204644460b74346b03c0be363a1bf541f57c537
parent21551737776ed5f7f80c2f661114655baa359166 (diff)
New communication interface with bareon instancemitaka-eol
Set of tools that represent some sort of API to communicate with bareon instance. This API use vendor_passthru ironic API to "catch" request from bareon instance to ironic API. So bareon-ironic driver can receive bareon insnstance requests. This is existing communication channel. Before it was used to receive notification from bareon instance about successfull node load. Now this channel is extended to send "generic" tasks(step) from bareon-ironic driver to bareon instance. Right now only one task(step) is used - step to inject SSH key into bareon instance. This new "steps" interface allow to refuse from preinstalled SSH key in bareon instance, right now. And in future it allow to refuse from SSH communication between bareon-ironic and bareon instance... Change-Id: I0791807c7cb3dba70c71c4f46e5eddf01da76cdd
Notes
Notes (review): Code-Review+1: Doug Szumski <dszumski@cray.com> Code-Review+2: Oleksandr Berezovskyi <berezovskyi.oleksandr@gmail.com> Verified+1: Oleksandr Berezovskyi <berezovskyi.oleksandr@gmail.com> Workflow+1: Oleksandr Berezovskyi <berezovskyi.oleksandr@gmail.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Fri, 03 Mar 2017 10:19:07 +0000 Reviewed-on: https://review.openstack.org/435991 Project: openstack/bareon-ironic Branch: refs/heads/master
-rw-r--r--bareon_ironic/modules/bareon_base.py270
-rw-r--r--bareon_ironic/modules/bareon_exception.py20
-rw-r--r--bareon_ironic/tests/base.py11
-rw-r--r--bareon_ironic/tests/modules/test_bareon_base.py109
-rw-r--r--patches/newton/ironic/0003-Allow-access-to-bareon-ironic-vendor-passthru-API-en.patch44
-rw-r--r--setup.cfg2
6 files changed, 386 insertions, 70 deletions
diff --git a/bareon_ironic/modules/bareon_base.py b/bareon_ironic/modules/bareon_base.py
index 4393bdb..d236c67 100644
--- a/bareon_ironic/modules/bareon_base.py
+++ b/bareon_ironic/modules/bareon_base.py
@@ -1,7 +1,5 @@
1# 1#
2# Copyright 2015 Mirantis, Inc. 2# Copyright 2017 Cray Inc., All Rights Reserved
3#
4# Copyright 2016 Cray Inc., All Rights Reserved
5# 3#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain 5# not use this file except in compliance with the License. You may obtain
@@ -19,8 +17,13 @@
19Bareon deploy driver. 17Bareon deploy driver.
20""" 18"""
21 19
20import abc
21import inspect
22import json 22import json
23import os 23import os
24import pprint
25import stat
26import sys
24 27
25import eventlet 28import eventlet
26import pkg_resources 29import pkg_resources
@@ -35,7 +38,6 @@ from ironic.common import boot_devices
35from ironic.common import exception 38from ironic.common import exception
36from ironic.common import states 39from ironic.common import states
37from ironic.common.i18n import _ 40from ironic.common.i18n import _
38from ironic.common.i18n import _LE
39from ironic.common.i18n import _LI 41from ironic.common.i18n import _LI
40from ironic.conductor import task_manager 42from ironic.conductor import task_manager
41from ironic.conductor import utils as manager_utils 43from ironic.conductor import utils as manager_utils
@@ -496,16 +498,13 @@ class BareonVendor(base.VendorInterface):
496 :param task: a TaskManager instance 498 :param task: a TaskManager instance
497 :param method: method to be validated 499 :param method: method to be validated
498 """ 500 """
499 if method == 'exec_actions': 501 if method in ('exec_actions', 'deploy_steps'):
500 return 502 return
501 503
502 if method == 'switch_boot': 504 if method == 'switch_boot':
503 self.validate_switch_boot(task, **kwargs) 505 self.validate_switch_boot(task, **kwargs)
504 return 506 return
505 507
506 if not kwargs.get('status'):
507 raise exception.MissingParameterValue(_('Unknown Bareon status'
508 ' on a node.'))
509 if not kwargs.get('address'): 508 if not kwargs.get('address'):
510 raise exception.MissingParameterValue(_('Bareon must pass ' 509 raise exception.MissingParameterValue(_('Bareon must pass '
511 'address of a node.')) 510 'address of a node.'))
@@ -520,22 +519,46 @@ class BareonVendor(base.VendorInterface):
520 raise exception.MissingParameterValue(_('No ssh user info ' 519 raise exception.MissingParameterValue(_('No ssh user info '
521 'passed.')) 520 'passed.'))
522 521
522 @base.passthru(['GET', 'POST'], async=False)
523 def deploy_steps(self, task, **data):
524 http_method = data.pop('http_method')
525 driver_info = _NodeDriverInfoAdapter(task.node)
526
527 if http_method == 'GET':
528 ssh_keys_step = _InjectSSHKeyStepRequest(task, driver_info)
529 return ssh_keys_step()
530
531 steps_mapping = _DeployStepMapping()
532 data = _DeployStepsAdapter(data)
533 try:
534 request_cls = steps_mapping.name_to_step[data.action]
535 except KeyError:
536 if data.action is not None:
537 raise RuntimeError(
538 'There is no name mapping for deployment step: '
539 '{!r}'.format(data.action))
540
541 message = (
542 'Bareon\'s callback service have failed with internall error')
543 if data.status_details:
544 message += '\nFailure details: {}'.format(
545 pprint.pformat(data.status_details))
546 # TODO(dbogun): add support for existing log extraction mechanism
547 deploy_utils.set_failed_state(
548 task, message, collect_logs=False)
549 else:
550 handler = request_cls.result_handler(
551 task, driver_info, data)
552 handler()
553
554 return {'url': None}
555
523 @base.passthru(['POST']) 556 @base.passthru(['POST'])
524 @task_manager.require_exclusive_lock 557 @task_manager.require_exclusive_lock
525 def pass_deploy_info(self, task, **kwargs): 558 def pass_deploy_info(self, task, **kwargs):
526 """Continues the deployment of baremetal node.""" 559 """Continues the deployment of baremetal node."""
527 node = task.node 560 node = task.node
528 task.process_event('resume') 561 task.process_event('resume')
529 err_msg = _('Failed to continue deployment with Bareon.')
530
531 agent_status = kwargs.get('status')
532 if agent_status != 'ready':
533 LOG.error(_LE('Deploy failed for node %(node)s. Bareon is not '
534 'in ready state, error: %(error)s'),
535 {'node': node.uuid,
536 'error': kwargs.get('error_message')})
537 deploy_utils.set_failed_state(task, err_msg)
538 return
539 562
540 driver_info = _NodeDriverInfoAdapter(task.node) 563 driver_info = _NodeDriverInfoAdapter(task.node)
541 564
@@ -618,7 +641,7 @@ class BareonVendor(base.VendorInterface):
618 641
619 def _deploy_failed(self, task, msg): 642 def _deploy_failed(self, task, msg):
620 LOG.error(msg) 643 LOG.error(msg)
621 deploy_utils.set_failed_state(task, msg) 644 deploy_utils.set_failed_state(task, msg, collect_logs=False)
622 645
623 def _check_bareon_version(self, ssh, node_uuid): 646 def _check_bareon_version(self, ssh, node_uuid):
624 try: 647 try:
@@ -972,62 +995,193 @@ def get_tenant_images_json_path(node):
972 "tenant_images.json") 995 "tenant_images.json")
973 996
974 997
998class _AbstractAdapter(object):
999 def __init__(self, data):
1000 self._raw = data
1001
1002 def _extract_fields(self, mapping):
1003 for attr, name in mapping:
1004 try:
1005 value = self._raw[name]
1006 except KeyError:
1007 continue
1008 setattr(self, attr, value)
1009
1010
1011class _DeployStepsAdapter(_AbstractAdapter):
1012 action = action_payload = None
1013 status = status_details = None
1014
1015 def __init__(self, data):
1016 super(_DeployStepsAdapter, self).__init__(data)
1017
1018 self._extract_fields({
1019 'action': 'name',
1020 'status': 'status'}.items())
1021 self.action_payload = self._raw.get('payload', {})
1022 self.status_details = self._raw.get('status-details', '')
1023
1024
975# TODO(dbogun): handle all driver_info keys 1025# TODO(dbogun): handle all driver_info keys
976class _NodeDriverInfoAdapter(object): 1026class _NodeDriverInfoAdapter(_AbstractAdapter):
1027 _exc_prefix = 'driver_info: '
1028
977 ssh_port = None 1029 ssh_port = None
978 # TODO(dbogun): check API way to defined access defaults 1030 # TODO(dbogun): check API way to defined access defaults
979 ssh_login = 'root' 1031 ssh_login = 'root'
980 ssh_key = '/etc/ironic/bareon_key' 1032 ssh_key = '/etc/ironic/bareon_key'
1033 ssh_key_pub = None
981 entry_point = 'bareon-provision' 1034 entry_point = 'bareon-provision'
982 1035
983 def __init__(self, node): 1036 def __init__(self, node):
1037 super(_NodeDriverInfoAdapter, self).__init__(node.driver_info)
984 self.node = node 1038 self.node = node
985 self._raw = self.node.driver_info
986 self._errors = []
987 1039
988 self._extract_ssh_port() 1040 self._extract_fields({
989 self._extract_optional_parameters() 1041 'ssh_port': 'bareon_ssh_port',
1042 'ssh_key': 'bareon_key_filename',
1043 'ssh_key_pub': 'bareon_public_key_filename',
1044 'ssh_login': 'bareon_username',
1045 'entry_point': 'bareon_deploy_script'}.items())
1046 self._process()
990 self._validate() 1047 self._validate()
991 1048
992 if self._errors: 1049 def _process(self):
993 raise exception.InvalidParameterValue(_( 1050 if self.ssh_key_pub is None:
994 'The following errors were encountered while parsing ' 1051 self.ssh_key_pub = '{}.pub'.format(self.ssh_key)
995 'driver_info:\n {}').format(' \n'.join(self._errors)))
996 1052
997 def _extract_ssh_port(self): 1053 if self.ssh_port is not None:
998 key = 'bareon_ssh_port' 1054 self.ssh_port = int(self.ssh_port)
999 try: 1055 if not 0 < self.ssh_port < 65536:
1000 port = self._raw[key] 1056 raise exception.InvalidParameterValue(
1001 port = int(port) 1057 '{}Invalid SSH port number({}) is outside of allowed '
1002 if not 0 < port < 65536: 1058 'range.'.format(self._exc_prefix, 'bareon_ssh_port'))
1003 raise ValueError(
1004 'Port number {} is outside of allowed range'.format(port))
1005 except KeyError:
1006 port = None
1007 except ValueError as e:
1008 self._errors.append('{}: {}'.format(key, str(e)))
1009 return
1010
1011 self.ssh_port = port
1012
1013 def _extract_optional_parameters(self):
1014 for attr, name in (
1015 ('ssh_key', 'bareon_key_filename'),
1016 ('ssh_login', 'bareon_username'),
1017 ('entry_point', 'bareon_deploy_script')):
1018 try:
1019 value = self._raw[name]
1020 except KeyError:
1021 continue
1022 setattr(self, attr, value)
1023 1059
1024 def _validate(self): 1060 def _validate(self):
1025 self._validate_ssh_key() 1061 self._validate_ssh_key()
1026 1062
1027 def _validate_ssh_key(self): 1063 def _validate_ssh_key(self):
1064 missing = []
1065 pkey_stats = None
1066 for idx, target in enumerate((self.ssh_key, self.ssh_key_pub)):
1067 try:
1068 target_stat = os.stat(target)
1069 if not idx:
1070 pkey_stats = target_stat
1071 except OSError as e:
1072 missing.append(e)
1073
1074 missing = ['{0.filename}: {0.strerror}'.format(x) for x in missing]
1075 if missing:
1076 raise exception.InvalidParameterValue(
1077 '{}Unable to use SSH key:\n{}'.format(
1078 self._exc_prefix, '\n'.join(missing)))
1079
1080 issue = None
1081 if not stat.S_ISREG(pkey_stats.st_mode):
1082 issue = 'SSH private key {!r} is not a regular file.'.format(
1083 self.ssh_key)
1084 if pkey_stats.st_mode & 0o177:
1085 issue = 'Permissions {} for {!r} are too open.'.format(
1086 oct(pkey_stats.st_mode & 0o777), self.ssh_key)
1087
1088 if issue:
1089 raise exception.InvalidParameterValue(issue)
1090
1091
1092@six.add_metaclass(abc.ABCMeta)
1093class _AbstractDeployStepHandler(object):
1094 def __init__(self, task, driver_info):
1095 self.task = task
1096 self.driver_info = driver_info
1097
1098 @abc.abstractmethod
1099 def __call__(self):
1100 pass
1101
1102
1103@six.add_metaclass(abc.ABCMeta)
1104class _AbstractDeployStepResult(_AbstractDeployStepHandler):
1105 def __init__(self, task, driver_info, step_info):
1106 super(_AbstractDeployStepResult, self).__init__(task, driver_info)
1107 self.step_info = step_info
1108
1109 def __call__(self):
1110 if not self.step_info.status:
1111 self._handle_error()
1112 return
1113
1114 return self._handle()
1115
1116 @abc.abstractmethod
1117 def _handle(self):
1118 pass
1119
1120 def _handle_error(self):
1121 message = 'Deployment step "{}" have failed: {}'.format(
1122 self.step_info.action, self.step_info.status_details)
1123 # TODO(dbogun): add support for existing log extraction mechanism
1124 deploy_utils.set_failed_state(self.task, message, collect_logs=False)
1125
1126
1127@six.add_metaclass(abc.ABCMeta)
1128class _AbstractDeployStepRequest(_AbstractDeployStepHandler):
1129 @abc.abstractproperty
1130 def name(self):
1131 pass
1132
1133 @abc.abstractproperty
1134 def result_handler(self):
1135 pass
1136
1137 def __call__(self):
1138 payload = self._handle()
1139 return {
1140 'name': self.name,
1141 'payload': payload}
1142
1143 @abc.abstractmethod
1144 def _handle(self):
1145 pass
1146
1147
1148class _InjectSSHKeyStepResult(_AbstractDeployStepResult):
1149 def _handle(self):
1150 pass
1151
1152
1153class _InjectSSHKeyStepRequest(_AbstractDeployStepRequest):
1154 name = 'inject-ssh-keys'
1155 result_handler = _InjectSSHKeyStepResult
1156
1157 def _handle(self):
1028 try: 1158 try:
1029 open(self.ssh_key).close() 1159 with open(self.driver_info.ssh_key_pub) as data:
1160 ssh_key = data.read()
1030 except IOError as e: 1161 except IOError as e:
1031 self._errors.append( 1162 raise bareon_exception.DeployTaskError(
1032 'Unable to use "{key}" as ssh private key: ' 1163 name=type(self).__name__, details=e)
1033 '{e.strerror} (errno={e.errno})'.format(key=self.ssh_key, e=e)) 1164
1165 return {
1166 'ssh-keys': {
1167 self.driver_info.ssh_login: [ssh_key]}}
1168
1169
1170class _DeployStepMapping(object):
1171 def __init__(self):
1172 self.steps = []
1173
1174 base_cls = _AbstractDeployStepRequest
1175 target = sys.modules[__name__]
1176 for name in dir(target):
1177 value = getattr(target, name)
1178 if (inspect.isclass(value)
1179 and issubclass(value, base_cls)
1180 and value is not base_cls):
1181 self.steps.append(value)
1182
1183 self.name_to_step = {}
1184 self.step_to_name = {}
1185 for task in self.steps:
1186 self.name_to_step[task.name] = task
1187 self.step_to_name[task] = task.name
diff --git a/bareon_ironic/modules/bareon_exception.py b/bareon_ironic/modules/bareon_exception.py
index 9e187b1..dcf5883 100644
--- a/bareon_ironic/modules/bareon_exception.py
+++ b/bareon_ironic/modules/bareon_exception.py
@@ -1,5 +1,5 @@
1# 1#
2# Copyright 2016 Cray Inc., All Rights Reserved 2# Copyright 2017 Cray Inc., All Rights Reserved
3# 3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain 5# not use this file except in compliance with the License. You may obtain
@@ -15,6 +15,8 @@
15 15
16"""Bareon driver exceptions""" 16"""Bareon driver exceptions"""
17 17
18import pprint
19
18from ironic.common import exception 20from ironic.common import exception
19from ironic.common.i18n import _ 21from ironic.common.i18n import _
20 22
@@ -46,3 +48,19 @@ class DeployTerminationSucceed(exception.IronicException):
46 48
47class BootSwitchFailed(exception.IronicException): 49class BootSwitchFailed(exception.IronicException):
48 message = _("Boot switch failed. Error: %(error)s") 50 message = _("Boot switch failed. Error: %(error)s")
51
52
53class DeployProtocolError(exception.IronicException):
54 _msg_fmt = _('Corrupted deploy protocol message: %(details)s\n%(payload)s')
55
56 def __init__(self, message=None, **substitute):
57 payload = substitute.pop('message', {})
58 payload = pprint.pformat(payload)
59
60 super(DeployProtocolError, self).__init__(
61 message, payload=payload, **substitute)
62
63
64class DeployTaskError(exception.IronicException):
65 _msg_fmt = _(
66 'Node deploy task "%(name)s" have failed: %(details)s')
diff --git a/bareon_ironic/tests/base.py b/bareon_ironic/tests/base.py
index 6ab9deb..ceeee9d 100644
--- a/bareon_ironic/tests/base.py
+++ b/bareon_ironic/tests/base.py
@@ -1,5 +1,5 @@
1# 1#
2# Copyright 2016 Cray Inc., All Rights Reserved 2# Copyright 2017 Cray Inc., All Rights Reserved
3# 3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain 5# not use this file except in compliance with the License. You may obtain
@@ -13,6 +13,8 @@
13# License for the specific language governing permissions and limitations 13# License for the specific language governing permissions and limitations
14# under the License. 14# under the License.
15 15
16import fixtures
17
16from ironic.tests import base 18from ironic.tests import base
17from ironic.tests.unit.db import base as db_base 19from ironic.tests.unit.db import base as db_base
18 20
@@ -22,4 +24,9 @@ class AbstractTestCase(base.TestCase):
22 24
23 25
24class AbstractDBTestCase(db_base.DbTestCase): 26class AbstractDBTestCase(db_base.DbTestCase):
25 pass 27 def setUp(self):
28 super(AbstractDBTestCase, self).setUp()
29
30 self.config(enabled_drivers=['bare_swift_ssh'])
31
32 self.temp_dir = self.useFixture(fixtures.TempDir())
diff --git a/bareon_ironic/tests/modules/test_bareon_base.py b/bareon_ironic/tests/modules/test_bareon_base.py
index ffd1c25..9ee91d9 100644
--- a/bareon_ironic/tests/modules/test_bareon_base.py
+++ b/bareon_ironic/tests/modules/test_bareon_base.py
@@ -1,5 +1,5 @@
1# 1#
2# Copyright 2016 Cray Inc., All Rights Reserved 2# Copyright 2017 Cray Inc., All Rights Reserved
3# 3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain 5# not use this file except in compliance with the License. You may obtain
@@ -14,6 +14,7 @@
14# under the License. 14# under the License.
15 15
16import json 16import json
17import os
17 18
18import fixtures 19import fixtures
19import mock 20import mock
@@ -32,13 +33,6 @@ SWIFT_DEPLOY_IMAGE_MODE = resources.PullSwiftTempurlResource.MODE
32 33
33 34
34class BareonBaseTestCase(base.AbstractDBTestCase): 35class BareonBaseTestCase(base.AbstractDBTestCase):
35 def setUp(self):
36 super(BareonBaseTestCase, self).setUp()
37
38 self.config(enabled_drivers=['bare_swift_ssh'])
39
40 self.temp_dir = self.useFixture(fixtures.TempHomeDir())
41
42 @mock.patch.object(bareon_base.BareonDeploy, 36 @mock.patch.object(bareon_base.BareonDeploy,
43 "_get_deploy_driver", 37 "_get_deploy_driver",
44 mock.Mock(return_value="test_driver")) 38 mock.Mock(return_value="test_driver"))
@@ -239,6 +233,105 @@ class TestDeploymentConfigValidator(base.AbstractTestCase):
239 self.tmpdir.join('corrupted.json')) 233 self.tmpdir.join('corrupted.json'))
240 234
241 235
236class TestVendorDeployment(base.AbstractDBTestCase):
237 temp_dir = None
238
239 ssh_key = ssh_key_pub = node = None
240 ssh_key_payload = 'SSH KEY (private)'
241 ssh_key_pub_payload = 'SSH KEY (public)'
242
243 def test_deploy_steps_get(self):
244 vendor_iface = bareon_base.BareonVendor()
245 args = {
246 'http_method': 'GET'}
247
248 with task_manager.acquire(
249 self.context, self.node.uuid,
250 driver_name='bare_swift_ssh') as task:
251 step = vendor_iface.deploy_steps(task, **args)
252
253 expected_step = {
254 'name': 'inject-ssh-keys',
255 'payload': {
256 'ssh-keys': {
257 'root': [self.ssh_key_pub_payload]}}}
258
259 self.assertEqual(expected_step, step)
260
261 @mock.patch.object(bareon_base._InjectSSHKeyStepResult, '_handle')
262 def test_deploy_steps_post(self, result_handler):
263 vendor_iface = bareon_base.BareonVendor()
264 args = {
265 'http_method': 'POST',
266 'name': 'inject-ssh-keys',
267 'status': True}
268
269 with task_manager.acquire(
270 self.context, self.node.uuid,
271 driver_name='bare_swift_ssh') as task:
272 step = vendor_iface.deploy_steps(task, **args)
273
274 expected_step = {'url': None}
275
276 self.assertEqual(expected_step, step)
277 self.assertEqual(1, result_handler.call_count)
278
279 @mock.patch('ironic.drivers.modules.deploy_utils.set_failed_state')
280 def test_deploy_steps_post_fail(self, set_failed_state):
281 vendor_iface = bareon_base.BareonVendor()
282 args = {
283 'http_method': 'POST',
284 'name': 'inject-ssh-keys',
285 'status': False,
286 'status-details': 'Error during step execution.'}
287
288 with task_manager.acquire(
289 self.context, self.node.uuid,
290 driver_name='bare_swift_ssh') as task:
291 step = vendor_iface.deploy_steps(task, **args)
292
293 expected_step = {'url': None}
294
295 self.assertEqual(expected_step, step)
296 self.assertEqual(1, set_failed_state.call_count)
297
298 @mock.patch('ironic.drivers.modules.deploy_utils.set_failed_state')
299 def test_deploy_steps_post_fail_unbinded(self, set_failed_state):
300 vendor_iface = bareon_base.BareonVendor()
301 args = {
302 'http_method': 'POST',
303 'name': None,
304 'status': False,
305 'status-details': 'Error during step execution.'}
306
307 with task_manager.acquire(
308 self.context, self.node.uuid,
309 driver_name='bare_swift_ssh') as task:
310 step = vendor_iface.deploy_steps(task, **args)
311
312 expected_step = {'url': None}
313
314 self.assertEqual(expected_step, step)
315 self.assertEqual(1, set_failed_state.call_count)
316
317 def setUp(self):
318 super(TestVendorDeployment, self).setUp()
319
320 self.ssh_key = self.temp_dir.join('bareon-ssh.key')
321 self.ssh_key_pub = self.ssh_key + '.pub'
322
323 open(self.ssh_key, 'wt').write('SSH KEY (private)')
324 open(self.ssh_key_pub, 'wt').write('SSH KEY (public)')
325
326 os.chmod(self.ssh_key, 0o600)
327
328 self.node = test_utils.create_test_node(
329 self.context,
330 driver_info={
331 'bareon_key_filename': self.ssh_key,
332 'bareon_username': 'root'})
333
334
242class DummyError(Exception): 335class DummyError(Exception):
243 @property 336 @property
244 def message(self): 337 def message(self):
diff --git a/patches/newton/ironic/0003-Allow-access-to-bareon-ironic-vendor-passthru-API-en.patch b/patches/newton/ironic/0003-Allow-access-to-bareon-ironic-vendor-passthru-API-en.patch
new file mode 100644
index 0000000..29a07e0
--- /dev/null
+++ b/patches/newton/ironic/0003-Allow-access-to-bareon-ironic-vendor-passthru-API-en.patch
@@ -0,0 +1,44 @@
1From 9787aa4765db729d05d8c9acff19adb4f9181189 Mon Sep 17 00:00:00 2001
2From: Dmitry Bogun <dbogun@mirantis.com>
3Date: Mon, 20 Feb 2017 16:31:34 +0200
4Subject: [PATCH 3/3] Allow access to bareon-ironic vendor passthru API
5 endpoints
6
7Bareon-ironic driver have implemented extended vendor passthru
8"protocol" used in communication between bareon-ironic driver and bareon
9instance. This "protoco" use one more http endpoint. This endpoint must
10be treated in same was as already existed enpoint
11vendor_passthru/pass_deploy_info
12---
13 ironic/api/config.py | 1 +
14 ironic/api/controllers/v1/node.py | 2 +-
15 2 files changed, 2 insertions(+), 1 deletion(-)
16
17diff --git a/ironic/api/config.py b/ironic/api/config.py
18index 49231d5..8627dc2 100644
19--- a/ironic/api/config.py
20+++ b/ironic/api/config.py
21@@ -36,6 +36,7 @@ app = {
22 '/v1/drivers/[a-z0-9_]*/vendor_passthru/lookup',
23 '/v1/nodes/[a-z0-9\-]+/vendor_passthru/heartbeat',
24 '/v1/nodes/[a-z0-9\-]+/vendor_passthru/pass_deploy_info',
25+ '/v1/nodes/[a-z0-9\-]+/vendor_passthru/deploy_steps',
26 ],
27 }
28
29diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py
30index d93c2c4..9422772 100644
31--- a/ironic/api/controllers/v1/node.py
32+++ b/ironic/api/controllers/v1/node.py
33@@ -980,7 +980,7 @@ class NodeVendorPassthruController(rest.RestController):
34 :param data: body of data to supply to the specified method.
35 """
36 cdict = pecan.request.context.to_dict()
37- if method in ('heartbeat', 'pass_deploy_info'):
38+ if method in ('heartbeat', 'pass_deploy_info', 'deploy_steps'):
39 policy.authorize('baremetal:node:ipa_heartbeat', cdict, cdict)
40 else:
41 policy.authorize('baremetal:node:vendor_passthru', cdict, cdict)
42--
432.10.2
44
diff --git a/setup.cfg b/setup.cfg
index 6c8de4a..176c080 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
1[metadata] 1[metadata]
2name = bareon-ironic 2name = bareon-ironic
3version = 1.0.0 3version = 1.0.1
4author = Cray Inc. 4author = Cray Inc.
5summary = Bareon-based deployment driver for Ironic 5summary = Bareon-based deployment driver for Ironic
6classifier = 6classifier =