1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13from unittest import mock
14
15from keystoneauth1 import adapter
16
17from openstack.baremetal.v1 import node as _node
18from openstack.baremetal_introspection.v1 import _proxy
19from openstack.baremetal_introspection.v1 import introspection
20from openstack import exceptions
21from openstack.tests.unit import base
22from openstack.tests.unit import test_proxy_base
23
24
25@mock.patch.object(introspection.Introspection, 'create', autospec=True)
26class TestStartIntrospection(base.TestCase):
27
28    def setUp(self):
29        super(TestStartIntrospection, self).setUp()
30        self.session = mock.Mock(spec=adapter.Adapter)
31        self.proxy = _proxy.Proxy(self.session)
32
33    def test_create_introspection(self, mock_create):
34        self.proxy.start_introspection('abcd')
35        mock_create.assert_called_once_with(mock.ANY, self.proxy)
36        introspect = mock_create.call_args[0][0]
37        self.assertEqual('abcd', introspect.id)
38
39    def test_create_introspection_with_node(self, mock_create):
40        self.proxy.start_introspection(_node.Node(id='abcd'))
41        mock_create.assert_called_once_with(mock.ANY, self.proxy)
42        introspect = mock_create.call_args[0][0]
43        self.assertEqual('abcd', introspect.id)
44
45    def test_create_introspection_manage_boot(self, mock_create):
46        self.proxy.start_introspection('abcd', manage_boot=False)
47        mock_create.assert_called_once_with(mock.ANY, self.proxy,
48                                            manage_boot=False)
49        introspect = mock_create.call_args[0][0]
50        self.assertEqual('abcd', introspect.id)
51
52
53class TestBaremetalIntrospectionProxy(test_proxy_base.TestProxyBase):
54
55    def setUp(self):
56        super(TestBaremetalIntrospectionProxy, self).setUp()
57        self.proxy = _proxy.Proxy(self.session)
58
59    def test_get_introspection(self):
60        self.verify_get(self.proxy.get_introspection,
61                        introspection.Introspection)
62
63
64@mock.patch('time.sleep', lambda _sec: None)
65@mock.patch.object(introspection.Introspection, 'fetch', autospec=True)
66class TestWaitForIntrospection(base.TestCase):
67
68    def setUp(self):
69        super(TestWaitForIntrospection, self).setUp()
70        self.session = mock.Mock(spec=adapter.Adapter)
71        self.proxy = _proxy.Proxy(self.session)
72        self.fake = {'state': 'waiting', 'error': None, 'finished': False}
73        self.introspection = introspection.Introspection(**self.fake)
74
75    def test_already_finished(self, mock_fetch):
76        self.introspection.is_finished = True
77        self.introspection.state = 'finished'
78        result = self.proxy.wait_for_introspection(self.introspection)
79        self.assertIs(result, self.introspection)
80        self.assertFalse(mock_fetch.called)
81
82    def test_wait(self, mock_fetch):
83        marker = [False]  # mutable object to modify in the closure
84
85        def _side_effect(allocation, session):
86            if marker[0]:
87                self.introspection.state = 'finished'
88                self.introspection.is_finished = True
89            else:
90                self.introspection.state = 'processing'
91                marker[0] = True
92
93        mock_fetch.side_effect = _side_effect
94        result = self.proxy.wait_for_introspection(self.introspection)
95        self.assertIs(result, self.introspection)
96        self.assertEqual(2, mock_fetch.call_count)
97
98    def test_timeout(self, mock_fetch):
99        self.assertRaises(exceptions.ResourceTimeout,
100                          self.proxy.wait_for_introspection,
101                          self.introspection,
102                          timeout=0.001)
103        mock_fetch.assert_called_with(self.introspection, self.proxy)
104
105    def test_failure(self, mock_fetch):
106        def _side_effect(allocation, session):
107            self.introspection.state = 'error'
108            self.introspection.is_finished = True
109            self.introspection.error = 'boom'
110
111        mock_fetch.side_effect = _side_effect
112        self.assertRaisesRegex(exceptions.ResourceFailure, 'boom',
113                               self.proxy.wait_for_introspection,
114                               self.introspection)
115        mock_fetch.assert_called_once_with(self.introspection, self.proxy)
116
117    def test_failure_ignored(self, mock_fetch):
118        def _side_effect(allocation, session):
119            self.introspection.state = 'error'
120            self.introspection.is_finished = True
121            self.introspection.error = 'boom'
122
123        mock_fetch.side_effect = _side_effect
124        result = self.proxy.wait_for_introspection(self.introspection,
125                                                   ignore_error=True)
126        self.assertIs(result, self.introspection)
127        mock_fetch.assert_called_once_with(self.introspection, self.proxy)
128
129
130@mock.patch.object(_proxy.Proxy, 'request', autospec=True)
131class TestAbortIntrospection(base.TestCase):
132
133    def setUp(self):
134        super(TestAbortIntrospection, self).setUp()
135        self.session = mock.Mock(spec=adapter.Adapter)
136        self.proxy = _proxy.Proxy(self.session)
137        self.fake = {'id': '1234', 'finished': False}
138        self.introspection = introspection.Introspection(**self.fake)
139
140    def test_abort(self, mock_request):
141        mock_request.return_value.status_code = 202
142        self.proxy.abort_introspection(self.introspection)
143        mock_request.assert_called_once_with(
144            self.proxy, 'introspection/1234/abort', 'POST',
145            headers=mock.ANY, microversion=mock.ANY,
146            retriable_status_codes=[409, 503])
147
148
149@mock.patch.object(_proxy.Proxy, 'request', autospec=True)
150class TestGetData(base.TestCase):
151
152    def setUp(self):
153        super(TestGetData, self).setUp()
154        self.session = mock.Mock(spec=adapter.Adapter)
155        self.proxy = _proxy.Proxy(self.session)
156        self.fake = {'id': '1234', 'finished': False}
157        self.introspection = introspection.Introspection(**self.fake)
158
159    def test_get_data(self, mock_request):
160        mock_request.return_value.status_code = 200
161        data = self.proxy.get_introspection_data(self.introspection)
162        mock_request.assert_called_once_with(
163            self.proxy, 'introspection/1234/data', 'GET',
164            headers=mock.ANY, microversion=mock.ANY)
165        self.assertIs(data, mock_request.return_value.json.return_value)
166
167    def test_get_unprocessed_data(self, mock_request):
168        mock_request.return_value.status_code = 200
169        data = self.proxy.get_introspection_data(self.introspection,
170                                                 processed=False)
171        mock_request.assert_called_once_with(
172            self.proxy, 'introspection/1234/data/unprocessed', 'GET',
173            headers=mock.ANY, microversion='1.17')
174        self.assertIs(data, mock_request.return_value.json.return_value)
175