summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ironic/common/molds.py112
-rw-r--r--ironic/conf/__init__.py2
-rw-r--r--ironic/conf/default.py21
-rw-r--r--ironic/conf/molds.py41
-rw-r--r--ironic/conf/opts.py1
-rw-r--r--ironic/drivers/utils.py85
-rw-r--r--ironic/tests/unit/common/test_molds.py291
-rw-r--r--ironic/tests/unit/drivers/test_utils.py268
8 files changed, 447 insertions, 374 deletions
diff --git a/ironic/common/molds.py b/ironic/common/molds.py
new file mode 100644
index 000000000..9c6a439bd
--- /dev/null
+++ b/ironic/common/molds.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2021 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_serialization import base64
+import requests
+import tenacity
+
+from ironic.common import exception
+from ironic.common.i18n import _
+from ironic.common import swift
+
+LOG = logging.getLogger(__name__)
+
+CONF = cfg.CONF
+
+
+def save_configuration(task, url, data):
+ """Store configuration mold to indicated location.
+
+ :param task: A TaskManager instance.
+ :param name: URL of the configuration item to save to.
+ :param data: Content of JSON data to save.
+
+ :raises IronicException: If using Swift storage and no authentication
+ token found in task's context.
+ :raises HTTPError: If failed to complete HTTP request.
+ """
+ @tenacity.retry(
+ retry=tenacity.retry_if_exception_type(
+ requests.exceptions.ConnectionError),
+ stop=tenacity.stop_after_attempt(CONF.molds.retry_attempts),
+ wait=tenacity.wait_fixed(CONF.molds.retry_interval),
+ reraise=True
+ )
+ def _request(url, data, auth_header):
+ return requests.put(
+ url, data=json.dumps(data, indent=2), headers=auth_header)
+
+ auth_header = _get_auth_header(task)
+ response = _request(url, data, auth_header)
+ response.raise_for_status()
+
+
+def get_configuration(task, url):
+ """Gets configuration mold from indicated location.
+
+ :param task: A TaskManager instance.
+ :param url: URL of the configuration item to get.
+
+ :returns: JSON configuration mold
+
+ :raises IronicException: If using Swift storage and no authentication
+ token found in task's context.
+ :raises HTTPError: If failed to complete HTTP request.
+ """
+ @tenacity.retry(
+ retry=tenacity.retry_if_exception_type(
+ requests.exceptions.ConnectionError),
+ stop=tenacity.stop_after_attempt(CONF.molds.retry_attempts),
+ wait=tenacity.wait_fixed(CONF.molds.retry_interval),
+ reraise=True
+ )
+ def _request(url, auth_header):
+ return requests.get(url, headers=auth_header)
+
+ auth_header = _get_auth_header(task)
+ response = _request(url, auth_header)
+ if response.status_code == requests.codes.ok:
+ return response.json()
+
+ response.raise_for_status()
+
+
+def _get_auth_header(task):
+ """Based on setup of configuration mold storage gets authentication header
+
+ :param task: A TaskManager instance.
+ :raises IronicException: If using Swift storage and no authentication
+ token found in task's context.
+ """
+ auth_header = None
+ if CONF.molds.storage == 'swift':
+ # TODO(ajya) Need to update to use Swift client and context session
+ auth_token = swift.get_swift_session().get_token()
+ if auth_token:
+ auth_header = {'X-Auth-Token': auth_token}
+ else:
+ raise exception.IronicException(
+ _('Missing auth_token for configuration mold access for node '
+ '%s') % task.node.uuid)
+ elif CONF.molds.storage == 'http':
+ if CONF.molds.user and CONF.molds.password:
+ auth_header = {'Authorization': 'Basic %s'
+ % base64.encode_as_text(
+ '%s:%s' % (CONF.molds.user,
+ CONF.molds.password))}
+ return auth_header
diff --git a/ironic/conf/__init__.py b/ironic/conf/__init__.py
index a1da1fb7e..2d6fab8db 100644
--- a/ironic/conf/__init__.py
+++ b/ironic/conf/__init__.py
@@ -38,6 +38,7 @@ from ironic.conf import irmc
from ironic.conf import iscsi
from ironic.conf import metrics
from ironic.conf import metrics_statsd
+from ironic.conf import molds
from ironic.conf import neutron
from ironic.conf import nova
from ironic.conf import pxe
@@ -72,6 +73,7 @@ iscsi.register_opts(CONF)
anaconda.register_opts(CONF)
metrics.register_opts(CONF)
metrics_statsd.register_opts(CONF)
+molds.register_opts(CONF)
neutron.register_opts(CONF)
nova.register_opts(CONF)
pxe.register_opts(CONF)
diff --git a/ironic/conf/default.py b/ironic/conf/default.py
index 0c41f73d0..bb5ec3918 100644
--- a/ironic/conf/default.py
+++ b/ironic/conf/default.py
@@ -423,26 +423,6 @@ webserver_opts = [
'with images.')),
]
-configuration_mold_opts = [
- cfg.StrOpt('mold_storage',
- default='swift',
- help=_('Configuration mold storage location. Supports "swift" '
- 'and "http". By default "swift".')),
- cfg.StrOpt('mold_user',
- help=_('User for "http" Basic auth. By default set empty.')),
- cfg.StrOpt('mold_password',
- help=_('Password for "http" Basic auth. By default set '
- 'empty.')),
- cfg.StrOpt('mold_retry_attempts',
- default=3,
- help=_('Retry attempts for saving or getting configuration '
- 'molds.')),
- cfg.StrOpt('mold_retry_interval',
- default=3,
- help=_('Retry interval for saving or getting configuration '
- 'molds.'))
-]
-
def register_opts(conf):
conf.register_opts(api_opts)
@@ -458,4 +438,3 @@ def register_opts(conf):
conf.register_opts(service_opts)
conf.register_opts(utils_opts)
conf.register_opts(webserver_opts)
- conf.register_opts(configuration_mold_opts)
diff --git a/ironic/conf/molds.py b/ironic/conf/molds.py
new file mode 100644
index 000000000..4cec1749a
--- /dev/null
+++ b/ironic/conf/molds.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2021 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_config import cfg
+
+from ironic.common.i18n import _
+
+opts = [
+ cfg.StrOpt('storage',
+ default='swift',
+ help=_('Configuration mold storage location. Supports "swift" '
+ 'and "http". By default "swift".')),
+ cfg.StrOpt('user',
+ help=_('User for "http" Basic auth. By default set empty.')),
+ cfg.StrOpt('password',
+ help=_('Password for "http" Basic auth. By default set '
+ 'empty.')),
+ cfg.StrOpt('retry_attempts',
+ default=3,
+ help=_('Retry attempts for saving or getting configuration '
+ 'molds.')),
+ cfg.StrOpt('retry_interval',
+ default=3,
+ help=_('Retry interval for saving or getting configuration '
+ 'molds.'))
+]
+
+
+def register_opts(conf):
+ conf.register_opts(opts, group='molds')
diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py
index 976611be6..eb61adf88 100644
--- a/ironic/conf/opts.py
+++ b/ironic/conf/opts.py
@@ -54,6 +54,7 @@ _opts = [
('anaconda', ironic.conf.anaconda.opts),
('metrics', ironic.conf.metrics.opts),
('metrics_statsd', ironic.conf.metrics_statsd.opts),
+ ('molds', ironic.conf.molds.opts),
('neutron', ironic.conf.neutron.list_opts()),
('nova', ironic.conf.nova.list_opts()),
('pxe', ironic.conf.pxe.opts),
diff --git a/ironic/drivers/utils.py b/ironic/drivers/utils.py
index 9e8a1611e..4aa2335d6 100644
--- a/ironic/drivers/utils.py
+++ b/ironic/drivers/utils.py
@@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import json
import os
import tempfile
@@ -21,8 +20,6 @@ from oslo_log import log as logging
from oslo_serialization import base64
from oslo_utils import strutils
from oslo_utils import timeutils
-import requests
-import tenacity
from ironic.common import exception
from ironic.common.i18n import _
@@ -387,85 +384,3 @@ OPTIONAL_PROPERTIES = {
"deprecated in favor of the new ones."
"Defaults to 'Default'. Optional."),
}
-
-
-def save_configuration_mold(task, url, data):
- """Store configuration mold to indicated location.
-
- :param task: A TaskManager instance.
- :param name: URL of the configuration item to save to.
- :param data: Content of JSON data to save.
-
- :raises IronicException: If using Swift storage and no authentication
- token found in task's context.
- :raises HTTPError: If failed to complete HTTP request.
- """
- @tenacity.retry(
- retry=tenacity.retry_if_exception_type(
- requests.exceptions.ConnectionError),
- stop=tenacity.stop_after_attempt(CONF.mold_retry_attempts),
- wait=tenacity.wait_fixed(CONF.mold_retry_interval),
- reraise=True
- )
- def _request(url, data, auth_header):
- return requests.put(
- url, data=json.dumps(data, indent=2), headers=auth_header)
-
- auth_header = _get_auth_header(task)
- response = _request(url, data, auth_header)
- response.raise_for_status()
-
-
-def get_configuration_mold(task, url):
- """Gets configuration mold from indicated location.
-
- :param task: A TaskManager instance.
- :param url: URL of the configuration item to get.
-
- :returns: JSON configuration mold
-
- :raises IronicException: If using Swift storage and no authentication
- token found in task's context.
- :raises HTTPError: If failed to complete HTTP request.
- """
- @tenacity.retry(
- retry=tenacity.retry_if_exception_type(
- requests.exceptions.ConnectionError),
- stop=tenacity.stop_after_attempt(CONF.mold_retry_attempts),
- wait=tenacity.wait_fixed(CONF.mold_retry_interval),
- reraise=True
- )
- def _request(url, auth_header):
- return requests.get(url, headers=auth_header)
-
- auth_header = _get_auth_header(task)
- response = _request(url, auth_header)
- if response.status_code == requests.codes.ok:
- return response.json()
-
- response.raise_for_status()
-
-
-def _get_auth_header(task):
- """Based on setup of configuration mold storage gets authentication header
-
- :param task: A TaskManager instance.
- :raises IronicException: If using Swift storage and no authentication
- token found in task's context.
- """
- auth_header = None
- if CONF.mold_storage == 'swift':
- # TODO(ajya) Need to update to use Swift client and context session
- auth_token = swift.get_swift_session().get_token()
- if auth_token:
- auth_header = {'X-Auth-Token': auth_token}
- else:
- raise exception.IronicException(
- _('Missing auth_token for configuration mold access for node '
- '%s') % task.node.uuid)
- elif CONF.mold_storage == 'http':
- if CONF.mold_user and CONF.mold_password:
- auth_header = {'Authorization': 'Basic %s'
- % base64.encode_as_text(
- '%s:%s' % (CONF.mold_user, CONF.mold_password))}
- return auth_header
diff --git a/ironic/tests/unit/common/test_molds.py b/ironic/tests/unit/common/test_molds.py
new file mode 100644
index 000000000..53c0ad0ac
--- /dev/null
+++ b/ironic/tests/unit/common/test_molds.py
@@ -0,0 +1,291 @@
+# Copyright (c) 2021 Dell Inc. or its subsidiaries.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from unittest import mock
+
+from oslo_config import cfg
+import requests
+
+from ironic.common import exception
+from ironic.common import molds
+from ironic.common import swift
+from ironic.conductor import task_manager
+from ironic.tests.unit.db import base as db_base
+from ironic.tests.unit.objects import utils as obj_utils
+
+
+class ConfigurationMoldTestCase(db_base.DbTestCase):
+
+ def setUp(self):
+ super(ConfigurationMoldTestCase, self).setUp()
+ self.node = obj_utils.create_test_node(self.context)
+
+ @mock.patch.object(swift, 'get_swift_session', autospec=True)
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_swift(self, mock_put, mock_swift):
+ mock_session = mock.Mock()
+ mock_session.get_token.return_value = 'token'
+ mock_swift.return_value = mock_session
+ cfg.CONF.molds.storage = 'swift'
+ url = 'https://example.com/file1'
+ data = {'key': 'value'}
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ molds.save_configuration(task, url, data)
+
+ mock_put.assert_called_once_with(url, '{\n "key": "value"\n}',
+ headers={'X-Auth-Token': 'token'})
+
+ @mock.patch.object(swift, 'get_swift_session', autospec=True)
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_swift_noauth(self, mock_put, mock_swift):
+ mock_session = mock.Mock()
+ mock_session.get_token.return_value = None
+ mock_swift.return_value = mock_session
+ cfg.CONF.molds.storage = 'swift'
+ url = 'https://example.com/file1'
+ data = {'key': 'value'}
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ exception.IronicException,
+ molds.save_configuration,
+ task, url, data)
+
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_http(self, mock_put):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ url = 'https://example.com/file1'
+ data = {'key': 'value'}
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ molds.save_configuration(task, url, data)
+
+ mock_put.assert_called_once_with(
+ url, '{\n "key": "value"\n}',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_http_noauth(self, mock_put):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = None
+ cfg.CONF.molds.password = None
+ url = 'https://example.com/file1'
+ data = {'key': 'value'}
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ molds.save_configuration(task, url, data)
+ mock_put.assert_called_once_with(
+ url, '{\n "key": "value"\n}',
+ headers=None)
+
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_http_error(self, mock_put):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ response = mock.MagicMock()
+ response.status_code = 404
+ response.raise_for_status.side_effect = requests.exceptions.HTTPError
+ mock_put.return_value = response
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ requests.exceptions.HTTPError,
+ molds.save_configuration,
+ task,
+ 'https://example.com/file2',
+ {'key': 'value'})
+ mock_put.assert_called_once_with(
+ 'https://example.com/file2', '{\n "key": "value"\n}',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_connection_error(self, mock_put):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ cfg.CONF.molds.retry_interval = 0
+ cfg.CONF.molds.retry_attempts = 3
+ response = mock.MagicMock()
+ mock_put.side_effect = [
+ requests.exceptions.ConnectTimeout,
+ requests.exceptions.ConnectionError,
+ response]
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ molds.save_configuration(
+ task, 'https://example.com/file2', {'key': 'value'})
+ mock_put.assert_called_with(
+ 'https://example.com/file2', '{\n "key": "value"\n}',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+ self.assertEqual(mock_put.call_count, 3)
+
+ @mock.patch.object(requests, 'put', autospec=True)
+ def test_save_configuration_connection_error_exceeded(self, mock_put):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ cfg.CONF.molds.retry_interval = 0
+ cfg.CONF.molds.retry_attempts = 2
+ mock_put.side_effect = [
+ requests.exceptions.ConnectTimeout,
+ requests.exceptions.ConnectionError]
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ requests.exceptions.ConnectionError,
+ molds.save_configuration,
+ task,
+ 'https://example.com/file2',
+ {'key': 'value'})
+ mock_put.assert_called_with(
+ 'https://example.com/file2', '{\n "key": "value"\n}',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+ self.assertEqual(mock_put.call_count, 2)
+
+ @mock.patch.object(swift, 'get_swift_session', autospec=True)
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_swift(self, mock_get, mock_swift):
+ mock_session = mock.Mock()
+ mock_session.get_token.return_value = 'token'
+ mock_swift.return_value = mock_session
+ cfg.CONF.molds.storage = 'swift'
+ response = mock.MagicMock()
+ response.status_code = 200
+ response.json.return_value = {'key': 'value'}
+ mock_get.return_value = response
+ url = 'https://example.com/file1'
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ result = molds.get_configuration(task, url)
+
+ mock_get.assert_called_once_with(
+ url, headers={'X-Auth-Token': 'token'})
+ self.assertJsonEqual({'key': 'value'}, result)
+
+ @mock.patch.object(swift, 'get_swift_session', autospec=True)
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_swift_noauth(self, mock_get, mock_swift):
+ mock_session = mock.Mock()
+ mock_session.get_token.return_value = None
+ mock_swift.return_value = mock_session
+ cfg.CONF.molds.storage = 'swift'
+ url = 'https://example.com/file1'
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ exception.IronicException,
+ molds.get_configuration,
+ task, url)
+
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_http(self, mock_get):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ response = mock.MagicMock()
+ response.status_code = 200
+ response.json.return_value = {'key': 'value'}
+ mock_get.return_value = response
+ url = 'https://example.com/file2'
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ result = molds.get_configuration(task, url)
+
+ mock_get.assert_called_once_with(
+ url, headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+ self.assertJsonEqual({"key": "value"}, result)
+
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_http_noauth(self, mock_get):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = None
+ cfg.CONF.molds.password = None
+ response = mock.MagicMock()
+ response.status_code = 200
+ response.json.return_value = {'key': 'value'}
+ mock_get.return_value = response
+ url = 'https://example.com/file2'
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ result = molds.get_configuration(task, url)
+
+ mock_get.assert_called_once_with(url, headers=None)
+ self.assertJsonEqual({"key": "value"}, result)
+
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_http_error(self, mock_get):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ response = mock.MagicMock()
+ response.status_code = 404
+ response.raise_for_status.side_effect = requests.exceptions.HTTPError
+ mock_get.return_value = response
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ requests.exceptions.HTTPError,
+ molds.get_configuration,
+ task,
+ 'https://example.com/file2')
+ mock_get.assert_called_once_with(
+ 'https://example.com/file2',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_connection_error(self, mock_get):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ cfg.CONF.molds.retry_interval = 0
+ cfg.CONF.molds.retry_attempts = 3
+ response = mock.MagicMock()
+ mock_get.side_effect = [
+ requests.exceptions.ConnectTimeout,
+ requests.exceptions.ConnectionError,
+ response]
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ molds.get_configuration(
+ task, 'https://example.com/file2')
+ mock_get.assert_called_with(
+ 'https://example.com/file2',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+ self.assertEqual(mock_get.call_count, 3)
+
+ @mock.patch.object(requests, 'get', autospec=True)
+ def test_get_configuration_mold_connection_error_exceeded(self, mock_get):
+ cfg.CONF.molds.storage = 'http'
+ cfg.CONF.molds.user = 'user'
+ cfg.CONF.molds.password = 'password'
+ cfg.CONF.molds.retry_interval = 0
+ cfg.CONF.molds.retry_attempts = 2
+ mock_get.side_effect = [
+ requests.exceptions.ConnectTimeout,
+ requests.exceptions.ConnectionError]
+
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ self.assertRaises(
+ requests.exceptions.ConnectionError,
+ molds.get_configuration,
+ task,
+ 'https://example.com/file2')
+ mock_get.assert_called_with(
+ 'https://example.com/file2',
+ headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
+ self.assertEqual(mock_get.call_count, 2)
diff --git a/ironic/tests/unit/drivers/test_utils.py b/ironic/tests/unit/drivers/test_utils.py
index 206a4340a..f2e79e827 100644
--- a/ironic/tests/unit/drivers/test_utils.py
+++ b/ironic/tests/unit/drivers/test_utils.py
@@ -19,7 +19,6 @@ from unittest import mock
from oslo_config import cfg
from oslo_utils import timeutils
-import requests
from ironic.common import exception
from ironic.common import swift
@@ -409,270 +408,3 @@ class MixinVendorInterfaceTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
self.vendor.validate,
task, method='fake_method')
-
-
-class ConfigurationMoldTestCase(db_base.DbTestCase):
-
- def setUp(self):
- super(ConfigurationMoldTestCase, self).setUp()
- self.node = obj_utils.create_test_node(self.context)
-
- @mock.patch.object(swift, 'get_swift_session', autospec=True)
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_swift(self, mock_put, mock_swift):
- mock_session = mock.Mock()
- mock_session.get_token.return_value = 'token'
- mock_swift.return_value = mock_session
- driver_utils.CONF.mold_storage = 'swift'
- url = 'https://example.com/file1'
- data = {'key': 'value'}
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- driver_utils.save_configuration_mold(task, url, data)
-
- mock_put.assert_called_once_with(url, '{\n "key": "value"\n}',
- headers={'X-Auth-Token': 'token'})
-
- @mock.patch.object(swift, 'get_swift_session', autospec=True)
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_swift_noauth(self, mock_put, mock_swift):
- mock_session = mock.Mock()
- mock_session.get_token.return_value = None
- mock_swift.return_value = mock_session
- driver_utils.CONF.mold_storage = 'swift'
- url = 'https://example.com/file1'
- data = {'key': 'value'}
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- exception.IronicException,
- driver_utils.save_configuration_mold,
- task, url, data)
-
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_http(self, mock_put):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- url = 'https://example.com/file1'
- data = {'key': 'value'}
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- driver_utils.save_configuration_mold(task, url, data)
-
- mock_put.assert_called_once_with(
- url, '{\n "key": "value"\n}',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
-
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_http_noauth(self, mock_put):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = None
- driver_utils.CONF.mold_password = None
- url = 'https://example.com/file1'
- data = {'key': 'value'}
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- driver_utils.save_configuration_mold(task, url, data)
-
- mock_put.assert_called_once_with(
- url, '{\n "key": "value"\n}',
- headers=None)
-
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_http_error(self, mock_put):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- response = mock.MagicMock()
- response.status_code = 404
- response.raise_for_status.side_effect = requests.exceptions.HTTPError
- mock_put.return_value = response
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- requests.exceptions.HTTPError,
- driver_utils.save_configuration_mold,
- task,
- 'https://example.com/file2',
- {'key': 'value'})
- mock_put.assert_called_once_with(
- 'https://example.com/file2', '{\n "key": "value"\n}',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
-
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_connection_error(self, mock_put):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- driver_utils.CONF.mold_retry_interval = 0
- driver_utils.CONF.mold_retry_attempts = 3
- response = mock.MagicMock()
- mock_put.side_effect = [
- requests.exceptions.ConnectTimeout,
- requests.exceptions.ConnectionError,
- response]
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- driver_utils.save_configuration_mold(
- task, 'https://example.com/file2', {'key': 'value'})
- mock_put.assert_called_with(
- 'https://example.com/file2', '{\n "key": "value"\n}',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
- self.assertEqual(mock_put.call_count, 3)
-
- @mock.patch.object(requests, 'put', autospec=True)
- def test_save_configuration_mold_connection_error_exceeded(self, mock_put):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- driver_utils.CONF.mold_retry_interval = 0
- driver_utils.CONF.mold_retry_attempts = 2
- mock_put.side_effect = [
- requests.exceptions.ConnectTimeout,
- requests.exceptions.ConnectionError]
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- requests.exceptions.ConnectionError,
- driver_utils.save_configuration_mold,
- task,
- 'https://example.com/file2',
- {'key': 'value'})
- mock_put.assert_called_with(
- 'https://example.com/file2', '{\n "key": "value"\n}',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
- self.assertEqual(mock_put.call_count, 2)
-
- @mock.patch.object(swift, 'get_swift_session', autospec=True)
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_swift(self, mock_get, mock_swift):
- mock_session = mock.Mock()
- mock_session.get_token.return_value = 'token'
- mock_swift.return_value = mock_session
- driver_utils.CONF.mold_storage = 'swift'
- response = mock.MagicMock()
- response.status_code = 200
- response.json.return_value = {'key': 'value'}
- mock_get.return_value = response
- url = 'https://example.com/file1'
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- result = driver_utils.get_configuration_mold(task, url)
-
- mock_get.assert_called_once_with(
- url, headers={'X-Auth-Token': 'token'})
- self.assertJsonEqual({'key': 'value'}, result)
-
- @mock.patch.object(swift, 'get_swift_session', autospec=True)
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_swift_noauth(self, mock_get, mock_swift):
- mock_session = mock.Mock()
- mock_session.get_token.return_value = None
- mock_swift.return_value = mock_session
- driver_utils.CONF.mold_storage = 'swift'
- url = 'https://example.com/file1'
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- exception.IronicException,
- driver_utils.get_configuration_mold,
- task, url)
-
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_http(self, mock_get):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- response = mock.MagicMock()
- response.status_code = 200
- response.json.return_value = {'key': 'value'}
- mock_get.return_value = response
- url = 'https://example.com/file2'
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- result = driver_utils.get_configuration_mold(task, url)
-
- mock_get.assert_called_once_with(
- url, headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
- self.assertJsonEqual({"key": "value"}, result)
-
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_http_noauth(self, mock_get):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = None
- driver_utils.CONF.mold_password = None
- response = mock.MagicMock()
- response.status_code = 200
- response.json.return_value = {'key': 'value'}
- mock_get.return_value = response
- url = 'https://example.com/file2'
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- result = driver_utils.get_configuration_mold(task, url)
-
- mock_get.assert_called_once_with(url, headers=None)
- self.assertJsonEqual({"key": "value"}, result)
-
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_http_error(self, mock_get):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- response = mock.MagicMock()
- response.status_code = 404
- response.raise_for_status.side_effect = requests.exceptions.HTTPError
- mock_get.return_value = response
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- requests.exceptions.HTTPError,
- driver_utils.get_configuration_mold,
- task,
- 'https://example.com/file2')
- mock_get.assert_called_once_with(
- 'https://example.com/file2',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
-
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_connection_error(self, mock_get):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- driver_utils.CONF.mold_retry_interval = 0
- driver_utils.CONF.mold_retry_attempts = 3
- response = mock.MagicMock()
- mock_get.side_effect = [
- requests.exceptions.ConnectTimeout,
- requests.exceptions.ConnectionError,
- response]
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- driver_utils.get_configuration_mold(
- task, 'https://example.com/file2')
- mock_get.assert_called_with(
- 'https://example.com/file2',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
- self.assertEqual(mock_get.call_count, 3)
-
- @mock.patch.object(requests, 'get', autospec=True)
- def test_get_configuration_mold_connection_error_exceeded(self, mock_get):
- driver_utils.CONF.mold_storage = 'http'
- driver_utils.CONF.mold_user = 'user'
- driver_utils.CONF.mold_password = 'password'
- driver_utils.CONF.mold_retry_interval = 0
- driver_utils.CONF.mold_retry_attempts = 2
- mock_get.side_effect = [
- requests.exceptions.ConnectTimeout,
- requests.exceptions.ConnectionError]
-
- with task_manager.acquire(self.context, self.node.uuid) as task:
- self.assertRaises(
- requests.exceptions.ConnectionError,
- driver_utils.get_configuration_mold,
- task,
- 'https://example.com/file2')
- mock_get.assert_called_with(
- 'https://example.com/file2',
- headers={'Authorization': 'Basic dXNlcjpwYXNzd29yZA=='})
- self.assertEqual(mock_get.call_count, 2)