Merge "Support credentials for virtual media"
This commit is contained in:
commit
97aa7a925c
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The emulator now supports providing ``UserName`` and ``Password`` for
|
||||
virtual media.
|
@ -411,9 +411,7 @@ def virtual_media_resource(identity, device):
|
||||
media_types = app.vmedia.get_device_media_types(
|
||||
identity, device)
|
||||
|
||||
(image_name, image_url, inserted,
|
||||
write_protected) = app.vmedia.get_device_image_info(
|
||||
identity, device)
|
||||
device_info = app.vmedia.get_device_image_info(identity, device)
|
||||
|
||||
except error.FishyError as ex:
|
||||
app.logger.warning(
|
||||
@ -430,10 +428,12 @@ def virtual_media_resource(identity, device):
|
||||
device=device,
|
||||
name=device_name,
|
||||
media_types=media_types,
|
||||
image_url=image_url,
|
||||
image_name=image_name,
|
||||
inserted=inserted,
|
||||
write_protected=write_protected
|
||||
image_url=device_info.image_url,
|
||||
image_name=device_info.image_name,
|
||||
inserted=device_info.inserted,
|
||||
write_protected=device_info.write_protected,
|
||||
username=device_info.username,
|
||||
password=device_info.password,
|
||||
)
|
||||
|
||||
|
||||
@ -444,7 +444,13 @@ def virtual_media_resource(identity, device):
|
||||
def virtual_media_insert(identity, device):
|
||||
image = flask.request.json.get('Image')
|
||||
inserted = flask.request.json.get('Inserted', True)
|
||||
write_protected = flask.request.json.get('WriteProtected', False)
|
||||
write_protected = flask.request.json.get('WriteProtected', True)
|
||||
username = flask.request.json.get('UserName', '')
|
||||
password = flask.request.json.get('Password', '')
|
||||
|
||||
if (not username and password) or (username and not password):
|
||||
message = "UserName and Password must be passed together"
|
||||
return flask.render_template('error.json', message=message), 400
|
||||
|
||||
manager = app.managers.get_manager(identity)
|
||||
systems = app.managers.get_managed_systems(manager)
|
||||
@ -453,7 +459,8 @@ def virtual_media_insert(identity, device):
|
||||
return '', 204
|
||||
|
||||
image_path = app.vmedia.insert_image(
|
||||
identity, device, image, inserted, write_protected)
|
||||
identity, device, image, inserted, write_protected,
|
||||
username=username, password=password)
|
||||
|
||||
for system in systems:
|
||||
try:
|
||||
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
@ -25,6 +26,12 @@ from sushy_tools.emulator.resources import base
|
||||
from sushy_tools import error
|
||||
|
||||
|
||||
DeviceInfo = collections.namedtuple(
|
||||
'DeviceInfo',
|
||||
['image_name', 'image_url', 'inserted', 'write_protected',
|
||||
'username', 'password'])
|
||||
|
||||
|
||||
class StaticDriver(base.DriverBase):
|
||||
"""Redfish virtual media simulator."""
|
||||
|
||||
@ -116,16 +123,19 @@ class StaticDriver(base.DriverBase):
|
||||
|
||||
:param identity: parent resource ID
|
||||
:param device: device name
|
||||
:returns: a `tuple` of: image name, image path, `True` is media is
|
||||
inserted, `True` if media is write-protected
|
||||
:returns: a `DeviceInfo` with: image name, image path,
|
||||
`True` is media is inserted, `True` if media is write-protected,
|
||||
user name and password
|
||||
:raises: `error.FishyError`
|
||||
"""
|
||||
device_info = self._get_device(identity, device)
|
||||
|
||||
return (device_info.get('ImageName', ''),
|
||||
device_info.get('Image', ''),
|
||||
device_info.get('Inserted', False),
|
||||
device_info.get('WriteProtected', False))
|
||||
return DeviceInfo(device_info.get('ImageName', ''),
|
||||
device_info.get('Image', ''),
|
||||
device_info.get('Inserted', False),
|
||||
device_info.get('WriteProtected', False),
|
||||
device_info.get('UserName', ''),
|
||||
device_info.get('Password', ''))
|
||||
|
||||
def _write_from_response(self, image_url, rsp, tmp_file):
|
||||
with open(tmp_file.name, 'wb') as fl:
|
||||
@ -152,7 +162,8 @@ class StaticDriver(base.DriverBase):
|
||||
return local_file
|
||||
|
||||
def insert_image(self, identity, device, image_url,
|
||||
inserted=True, write_protected=True):
|
||||
inserted=True, write_protected=True,
|
||||
username=None, password=None):
|
||||
"""Upload, remove or insert virtual media
|
||||
|
||||
:param identity: parent resource ID
|
||||
@ -166,10 +177,12 @@ class StaticDriver(base.DriverBase):
|
||||
device_info = self._get_device(identity, device)
|
||||
verify_media_cert = self._config.get(
|
||||
'SUSHY_EMULATOR_VMEDIA_VERIFY_SSL', True)
|
||||
auth = (username, password) if (username and password) else None
|
||||
|
||||
try:
|
||||
with requests.get(image_url,
|
||||
stream=True,
|
||||
auth=auth,
|
||||
verify=verify_media_cert) as rsp:
|
||||
if rsp.status_code >= 400:
|
||||
self._logger.error(
|
||||
@ -208,6 +221,8 @@ class StaticDriver(base.DriverBase):
|
||||
device_info['Image'] = local_file
|
||||
device_info['Inserted'] = inserted
|
||||
device_info['WriteProtected'] = write_protected
|
||||
device_info['UserName'] = username or ''
|
||||
device_info['Password'] = password or ''
|
||||
device_info['_local_file'] = local_file_path
|
||||
|
||||
self._devices.update({(identity, device): device_info})
|
||||
@ -227,6 +242,8 @@ class StaticDriver(base.DriverBase):
|
||||
device_info['ImageName'] = ''
|
||||
device_info['Inserted'] = False
|
||||
device_info['WriteProtected'] = False
|
||||
device_info['UserName'] = ''
|
||||
device_info['Password'] = ''
|
||||
|
||||
self._devices.update({(identity, device): device_info})
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"@odata.type": "#VirtualMedia.v1_1_0.VirtualMedia",
|
||||
"@odata.type": "#VirtualMedia.v1_3_0.VirtualMedia",
|
||||
"Id": {{ device|string|tojson }},
|
||||
"Name": {{ name|string|tojson }},
|
||||
"MediaTypes": [
|
||||
@ -21,7 +21,9 @@
|
||||
},
|
||||
"Oem": {}
|
||||
},
|
||||
"UserName": {{ username|string|tojson }},
|
||||
"Password": "{{ '******' if password else '' }}",
|
||||
"@odata.context": "/redfish/v1/$metadata#VirtualMedia.VirtualMedia",
|
||||
"@odata.id": {{ "/redfish/v1/Managers/%s/VirtualMedia/%s"|format(identity, device)|string|tojson }},
|
||||
"@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
def test_get_device_image_info(self):
|
||||
dev_info = self.test_driver.get_device_image_info(
|
||||
self.UUID, 'Cd')
|
||||
expected = ('', '', False, False)
|
||||
expected = ('', '', False, False, '', '')
|
||||
self.assertEqual(expected, dev_info)
|
||||
|
||||
@mock.patch.object(vmedia.StaticDriver, '_get_device', autospec=True)
|
||||
@ -99,7 +99,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
|
||||
self.assertEqual('/alphabet/soup/fish.iso', local_file)
|
||||
mock_requests.get.assert_called_once_with(
|
||||
'http://fish.it/red.iso', stream=True, verify=True)
|
||||
'http://fish.it/red.iso', stream=True, verify=True, auth=None)
|
||||
mock_open.assert_called_once_with(mock.ANY, 'wb')
|
||||
mock_rename.assert_called_once_with(
|
||||
'alphabet.soup', '/alphabet/soup/fish.iso')
|
||||
@ -107,6 +107,48 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
self.assertEqual('fish.iso', device_info['Image'])
|
||||
self.assertTrue(device_info['Inserted'])
|
||||
self.assertFalse(device_info['WriteProtected'])
|
||||
self.assertEqual('', device_info['UserName'])
|
||||
self.assertEqual('', device_info['Password'])
|
||||
self.assertEqual(local_file, device_info['_local_file'])
|
||||
|
||||
@mock.patch.object(vmedia.StaticDriver, '_get_device', autospec=True)
|
||||
@mock.patch.object(builtins, 'open', autospec=True)
|
||||
@mock.patch.object(vmedia.os, 'rename', autospec=True)
|
||||
@mock.patch.object(vmedia, 'tempfile', autospec=True)
|
||||
@mock.patch.object(vmedia, 'requests', autospec=True)
|
||||
def test_insert_image_auth(self, mock_requests, mock_tempfile, mock_rename,
|
||||
mock_open, mock_get_device):
|
||||
device_info = {}
|
||||
mock_get_device.return_value = device_info
|
||||
|
||||
mock_tempfile.mkdtemp.return_value = '/alphabet/soup'
|
||||
mock_tempfile.gettempdir.return_value = '/tmp'
|
||||
mock_tmp_file = (mock_tempfile.NamedTemporaryFile
|
||||
.return_value.__enter__.return_value)
|
||||
mock_tmp_file.name = 'alphabet.soup'
|
||||
mock_rsp = mock_requests.get.return_value.__enter__.return_value
|
||||
mock_rsp.headers = {
|
||||
'content-disposition': 'attachment; filename="fish.iso"'
|
||||
}
|
||||
mock_rsp.status_code = 200
|
||||
|
||||
local_file = self.test_driver.insert_image(
|
||||
self.UUID, 'Cd', 'http://fish.it/red.iso', inserted=True,
|
||||
write_protected=False, username='Admin', password='Secret')
|
||||
|
||||
self.assertEqual('/alphabet/soup/fish.iso', local_file)
|
||||
mock_requests.get.assert_called_once_with(
|
||||
'http://fish.it/red.iso', stream=True, verify=True,
|
||||
auth=('Admin', 'Secret'))
|
||||
mock_open.assert_called_once_with(mock.ANY, 'wb')
|
||||
mock_rename.assert_called_once_with(
|
||||
'alphabet.soup', '/alphabet/soup/fish.iso')
|
||||
|
||||
self.assertEqual('fish.iso', device_info['Image'])
|
||||
self.assertTrue(device_info['Inserted'])
|
||||
self.assertFalse(device_info['WriteProtected'])
|
||||
self.assertEqual('Admin', device_info['UserName'])
|
||||
self.assertEqual('Secret', device_info['Password'])
|
||||
self.assertEqual(local_file, device_info['_local_file'])
|
||||
|
||||
@mock.patch.object(vmedia.StaticDriver, '_get_device', autospec=True)
|
||||
@ -135,7 +177,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
|
||||
self.assertEqual('/alphabet/soup/red.iso', local_file)
|
||||
mock_requests.get.assert_called_once_with(
|
||||
'http://fish.it/red.iso', stream=True, verify=True)
|
||||
'http://fish.it/red.iso', stream=True, verify=True, auth=None)
|
||||
mock_open.assert_called_once_with(mock.ANY, 'wb')
|
||||
mock_rename.assert_called_once_with(
|
||||
'alphabet.soup', '/alphabet/soup/red.iso')
|
||||
@ -171,7 +213,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
|
||||
self.assertEqual('/alphabet/soup/boot-abc', local_file)
|
||||
mock_requests.get.assert_called_once_with(full_url, stream=True,
|
||||
verify=True)
|
||||
verify=True, auth=None)
|
||||
mock_open.assert_called_once_with(mock.ANY, 'wb')
|
||||
mock_rename.assert_called_once_with(
|
||||
'alphabet.soup', '/alphabet/soup/boot-abc')
|
||||
@ -215,7 +257,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
|
||||
self.assertEqual('/alphabet/soup/fish.iso', local_file)
|
||||
mock_requests.get.assert_called_once_with(
|
||||
'https://fish.it/red.iso', stream=True, verify=False)
|
||||
'https://fish.it/red.iso', stream=True, verify=False, auth=None)
|
||||
mock_open.assert_called_once_with(mock.ANY, 'wb')
|
||||
mock_rename.assert_called_once_with(
|
||||
'alphabet.soup', '/alphabet/soup/fish.iso')
|
||||
@ -251,7 +293,7 @@ class StaticDriverTestCase(base.BaseTestCase):
|
||||
self.UUID, 'Cd', 'http://fish.it/red.iso',
|
||||
inserted=True, write_protected=False)
|
||||
mock_requests.get.assert_called_once_with(
|
||||
'http://fish.it/red.iso', stream=True, verify=True)
|
||||
'http://fish.it/red.iso', stream=True, auth=None, verify=True)
|
||||
mock_open.assert_not_called()
|
||||
self.assertEqual({}, device_info)
|
||||
|
||||
|
@ -16,6 +16,7 @@ from unittest import mock
|
||||
from oslotest import base
|
||||
|
||||
from sushy_tools.emulator import main
|
||||
from sushy_tools.emulator.resources import vmedia
|
||||
from sushy_tools import error
|
||||
|
||||
|
||||
@ -491,19 +492,42 @@ class VirtualMediaTestCase(EmulatorTestCase):
|
||||
vmedia_mock.get_device_name.return_value = 'CD'
|
||||
vmedia_mock.get_device_media_types.return_value = [
|
||||
'CD', 'DVD']
|
||||
vmedia_mock.get_device_image_info.return_value = [
|
||||
'image-of-a-fish', 'fishy.iso', True, True]
|
||||
vmedia_mock.get_device_image_info.return_value = vmedia.DeviceInfo(
|
||||
'image-of-a-fish', 'fishy.iso', True, True, '', '')
|
||||
|
||||
response = self.app.get(
|
||||
'/redfish/v1/Managers/%s/VirtualMedia/CD' % self.uuid)
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(200, response.status_code, response.json)
|
||||
self.assertEqual('CD', response.json['Id'])
|
||||
self.assertEqual(['CD', 'DVD'], response.json['MediaTypes'])
|
||||
self.assertEqual('fishy.iso', response.json['Image'])
|
||||
self.assertEqual('image-of-a-fish', response.json['ImageName'])
|
||||
self.assertTrue(response.json['Inserted'])
|
||||
self.assertTrue(response.json['WriteProtected'])
|
||||
self.assertEqual('', response.json['UserName'])
|
||||
self.assertEqual('', response.json['Password'])
|
||||
|
||||
def test_virtual_media_with_auth(self, managers_mock, vmedia_mock):
|
||||
vmedia_mock = vmedia_mock.return_value
|
||||
vmedia_mock.get_device_name.return_value = 'CD'
|
||||
vmedia_mock.get_device_media_types.return_value = [
|
||||
'CD', 'DVD']
|
||||
vmedia_mock.get_device_image_info.return_value = vmedia.DeviceInfo(
|
||||
'image-of-a-fish', 'fishy.iso', True, True, 'Admin', 'Secret')
|
||||
|
||||
response = self.app.get(
|
||||
'/redfish/v1/Managers/%s/VirtualMedia/CD' % self.uuid)
|
||||
|
||||
self.assertEqual(200, response.status_code, response.json)
|
||||
self.assertEqual('CD', response.json['Id'])
|
||||
self.assertEqual(['CD', 'DVD'], response.json['MediaTypes'])
|
||||
self.assertEqual('fishy.iso', response.json['Image'])
|
||||
self.assertEqual('image-of-a-fish', response.json['ImageName'])
|
||||
self.assertTrue(response.json['Inserted'])
|
||||
self.assertTrue(response.json['WriteProtected'])
|
||||
self.assertEqual('Admin', response.json['UserName'])
|
||||
self.assertEqual('******', response.json['Password'])
|
||||
|
||||
def test_virtual_media_not_found(self, managers_mock, vmedia_mock):
|
||||
vmedia_mock.return_value.get_device_name.side_effect = error.FishyError
|
||||
|
Loading…
x
Reference in New Issue
Block a user