Move configuration mold utilities

Utilities moved to ironic.common.molds.
New config section [molds] created and settings moved there.

Change-Id: I1177f7dd5d5157bb3a5c0bd09acd75c9a394ab47
This commit is contained in:
Aija Jauntēva 2021-03-29 06:51:49 -04:00
parent b64cc44d1b
commit d913ef6396
8 changed files with 447 additions and 374 deletions

112
ironic/common/molds.py Normal file
View File

@ -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

View File

@ -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)

View File

@ -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)

41
ironic/conf/molds.py Normal file
View File

@ -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')

View File

@ -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),

View File

@ -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

View File

@ -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)

View File

@ -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)