Browse Source

New communication interface with bareon instance

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
tags/mitaka-eol^0
Dmitry Bogun 2 years ago
parent
commit
7468264200

+ 212
- 58
bareon_ironic/modules/bareon_base.py View File

@@ -1,7 +1,5 @@
1 1
 #
2
-# Copyright 2015 Mirantis, Inc.
3
-#
4
-# Copyright 2016 Cray Inc., All Rights Reserved
2
+# Copyright 2017 Cray Inc., All Rights Reserved
5 3
 #
6 4
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 5
 # not use this file except in compliance with the License. You may obtain
@@ -19,8 +17,13 @@
19 17
 Bareon deploy driver.
20 18
 """
21 19
 
20
+import abc
21
+import inspect
22 22
 import json
23 23
 import os
24
+import pprint
25
+import stat
26
+import sys
24 27
 
25 28
 import eventlet
26 29
 import pkg_resources
@@ -35,7 +38,6 @@ from ironic.common import boot_devices
35 38
 from ironic.common import exception
36 39
 from ironic.common import states
37 40
 from ironic.common.i18n import _
38
-from ironic.common.i18n import _LE
39 41
 from ironic.common.i18n import _LI
40 42
 from ironic.conductor import task_manager
41 43
 from ironic.conductor import utils as manager_utils
@@ -496,16 +498,13 @@ class BareonVendor(base.VendorInterface):
496 498
         :param task: a TaskManager instance
497 499
         :param method: method to be validated
498 500
         """
499
-        if method == 'exec_actions':
501
+        if method in ('exec_actions', 'deploy_steps'):
500 502
             return
501 503
 
502 504
         if method == 'switch_boot':
503 505
             self.validate_switch_boot(task, **kwargs)
504 506
             return
505 507
 
506
-        if not kwargs.get('status'):
507
-            raise exception.MissingParameterValue(_('Unknown Bareon status'
508
-                                                    ' on a node.'))
509 508
         if not kwargs.get('address'):
510 509
             raise exception.MissingParameterValue(_('Bareon must pass '
511 510
                                                     'address of a node.'))
@@ -520,22 +519,46 @@ class BareonVendor(base.VendorInterface):
520 519
             raise exception.MissingParameterValue(_('No ssh user info '
521 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 556
     @base.passthru(['POST'])
524 557
     @task_manager.require_exclusive_lock
525 558
     def pass_deploy_info(self, task, **kwargs):
526 559
         """Continues the deployment of baremetal node."""
527 560
         node = task.node
528 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 563
         driver_info = _NodeDriverInfoAdapter(task.node)
541 564
 
@@ -618,7 +641,7 @@ class BareonVendor(base.VendorInterface):
618 641
 
619 642
     def _deploy_failed(self, task, msg):
620 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 646
     def _check_bareon_version(self, ssh, node_uuid):
624 647
         try:
@@ -972,62 +995,193 @@ def get_tenant_images_json_path(node):
972 995
                         "tenant_images.json")
973 996
 
974 997
 
998
+class _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
+
1011
+class _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 1025
 # TODO(dbogun): handle all driver_info keys
976
-class _NodeDriverInfoAdapter(object):
1026
+class _NodeDriverInfoAdapter(_AbstractAdapter):
1027
+    _exc_prefix = 'driver_info: '
1028
+
977 1029
     ssh_port = None
978 1030
     # TODO(dbogun): check API way to defined access defaults
979 1031
     ssh_login = 'root'
980 1032
     ssh_key = '/etc/ironic/bareon_key'
1033
+    ssh_key_pub = None
981 1034
     entry_point = 'bareon-provision'
982 1035
 
983 1036
     def __init__(self, node):
1037
+        super(_NodeDriverInfoAdapter, self).__init__(node.driver_info)
984 1038
         self.node = node
985
-        self._raw = self.node.driver_info
986
-        self._errors = []
987 1039
 
988
-        self._extract_ssh_port()
989
-        self._extract_optional_parameters()
1040
+        self._extract_fields({
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 1047
         self._validate()
991 1048
 
992
-        if self._errors:
993
-            raise exception.InvalidParameterValue(_(
994
-                'The following errors were encountered while parsing '
995
-                'driver_info:\n  {}').format('  \n'.join(self._errors)))
1049
+    def _process(self):
1050
+        if self.ssh_key_pub is None:
1051
+            self.ssh_key_pub = '{}.pub'.format(self.ssh_key)
996 1052
 
997
-    def _extract_ssh_port(self):
998
-        key = 'bareon_ssh_port'
999
-        try:
1000
-            port = self._raw[key]
1001
-            port = int(port)
1002
-            if not 0 < port < 65536:
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)
1053
+        if self.ssh_port is not None:
1054
+            self.ssh_port = int(self.ssh_port)
1055
+            if not 0 < self.ssh_port < 65536:
1056
+                raise exception.InvalidParameterValue(
1057
+                    '{}Invalid SSH port number({}) is outside of allowed '
1058
+                    'range.'.format(self._exc_prefix, 'bareon_ssh_port'))
1023 1059
 
1024 1060
     def _validate(self):
1025 1061
         self._validate_ssh_key()
1026 1062
 
1027 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)
1093
+class _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)
1104
+class _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)
1128
+class _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
+
1148
+class _InjectSSHKeyStepResult(_AbstractDeployStepResult):
1149
+    def _handle(self):
1150
+        pass
1151
+
1152
+
1153
+class _InjectSSHKeyStepRequest(_AbstractDeployStepRequest):
1154
+    name = 'inject-ssh-keys'
1155
+    result_handler = _InjectSSHKeyStepResult
1156
+
1157
+    def _handle(self):
1028 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 1161
         except IOError as e:
1031
-            self._errors.append(
1032
-                'Unable to use "{key}" as ssh private key: '
1033
-                '{e.strerror} (errno={e.errno})'.format(key=self.ssh_key, e=e))
1162
+            raise bareon_exception.DeployTaskError(
1163
+                name=type(self).__name__, details=e)
1164
+
1165
+        return {
1166
+            'ssh-keys': {
1167
+                self.driver_info.ssh_login: [ssh_key]}}
1168
+
1169
+
1170
+class _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

+ 19
- 1
bareon_ironic/modules/bareon_exception.py View File

@@ -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 4
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 5
 #    not use this file except in compliance with the License. You may obtain
@@ -15,6 +15,8 @@
15 15
 
16 16
 """Bareon driver exceptions"""
17 17
 
18
+import pprint
19
+
18 20
 from ironic.common import exception
19 21
 from ironic.common.i18n import _
20 22
 
@@ -46,3 +48,19 @@ class DeployTerminationSucceed(exception.IronicException):
46 48
 
47 49
 class BootSwitchFailed(exception.IronicException):
48 50
     message = _("Boot switch failed. Error: %(error)s")
51
+
52
+
53
+class 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
+
64
+class DeployTaskError(exception.IronicException):
65
+    _msg_fmt = _(
66
+        'Node deploy task "%(name)s" have failed: %(details)s')

+ 9
- 2
bareon_ironic/tests/base.py View File

@@ -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 4
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 5
 # not use this file except in compliance with the License. You may obtain
@@ -13,6 +13,8 @@
13 13
 # License for the specific language governing permissions and limitations
14 14
 # under the License.
15 15
 
16
+import fixtures
17
+
16 18
 from ironic.tests import base
17 19
 from ironic.tests.unit.db import base as db_base
18 20
 
@@ -22,4 +24,9 @@ class AbstractTestCase(base.TestCase):
22 24
 
23 25
 
24 26
 class 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())

+ 101
- 8
bareon_ironic/tests/modules/test_bareon_base.py View File

@@ -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 4
 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 5
 # not use this file except in compliance with the License. You may obtain
@@ -14,6 +14,7 @@
14 14
 # under the License.
15 15
 
16 16
 import json
17
+import os
17 18
 
18 19
 import fixtures
19 20
 import mock
@@ -32,13 +33,6 @@ SWIFT_DEPLOY_IMAGE_MODE = resources.PullSwiftTempurlResource.MODE
32 33
 
33 34
 
34 35
 class 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 36
     @mock.patch.object(bareon_base.BareonDeploy,
43 37
                        "_get_deploy_driver",
44 38
                        mock.Mock(return_value="test_driver"))
@@ -239,6 +233,105 @@ class TestDeploymentConfigValidator(base.AbstractTestCase):
239 233
             self.tmpdir.join('corrupted.json'))
240 234
 
241 235
 
236
+class 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
+
242 335
 class DummyError(Exception):
243 336
     @property
244 337
     def message(self):

+ 44
- 0
patches/newton/ironic/0003-Allow-access-to-bareon-ironic-vendor-passthru-API-en.patch View File

@@ -0,0 +1,44 @@
1
+From 9787aa4765db729d05d8c9acff19adb4f9181189 Mon Sep 17 00:00:00 2001
2
+From: Dmitry Bogun <dbogun@mirantis.com>
3
+Date: Mon, 20 Feb 2017 16:31:34 +0200
4
+Subject: [PATCH 3/3] Allow access to bareon-ironic vendor passthru API
5
+ endpoints
6
+
7
+Bareon-ironic driver have implemented extended vendor passthru
8
+"protocol" used in communication between bareon-ironic driver and bareon
9
+instance. This "protoco" use one more http endpoint. This endpoint must
10
+be treated in same was as already existed enpoint
11
+vendor_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
+
17
+diff --git a/ironic/api/config.py b/ironic/api/config.py
18
+index 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
+ 
29
+diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py
30
+index 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
+-- 
43
+2.10.2
44
+

+ 1
- 1
setup.cfg View File

@@ -1,6 +1,6 @@
1 1
 [metadata]
2 2
 name = bareon-ironic
3
-version = 1.0.0
3
+version = 1.0.1
4 4
 author = Cray Inc.
5 5
 summary = Bareon-based deployment driver for Ironic
6 6
 classifier =

Loading…
Cancel
Save