Skip to content
This repository has been archived by the owner on Oct 15, 2020. It is now read-only.

Firmware bundle #392

Open
wants to merge 3 commits into
base: enhancement/resource_base_class
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# 4.7.1
# 4.7.1 (Unreleased)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets mark this release as 5.0 as it will be a major change because of design changes

#### Notes
Extends support of the SDK to OneView Rest API version 600 (OneView v4.0).

#### Features supported with current release:
- Firmware Bundle

#### Bug fixes
- [#364] (https://github.com/HewlettPackard/python-hpOneView/issues/364) Bug in index_resources.get_all()

Expand Down
2 changes: 1 addition & 1 deletion endpoints-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
|<sub>/rest/fcoe-networks/{id}</sub> | PUT | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
|<sub>/rest/fcoe-networks/{id}</sub> | DELETE | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark:
| **Firmware Bundles** |
|<sub>/rest/firmware-bundles</sub> | POST | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|<sub>/rest/firmware-bundles</sub> | POST | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| **Firmware Drivers** |
|<sub>/rest/firmware-drivers</sub> | GET | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|<sub>/rest/firmware-drivers</sub> | POST | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expand Down
7 changes: 5 additions & 2 deletions examples/firmware_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@
# To run this example you must define a path to a valid file
firmware_path = "<path_to_firmware_bundle>"

# Use the below option to specify additional request headers as required
custom_headers = {'initialScopeUris': '<Scope_Uris>'}

# Try load config from a file (if there is a config file)
config = try_load_from_file(config)
oneview_client = OneViewClient(config)

# Upload a firmware bundle
print("\nUpload a firmware bundle")
firmware_bundle_information = oneview_client.firmware_bundles.upload(file_path=firmware_path)
firmware_bundle_information = oneview_client.firmware_bundles.upload(file_path=firmware_path, custom_headers=custom_headers)
print("\n Upload successful! Firmware information returned: \n")
pprint(firmware_bundle_information)
pprint(firmware_bundle_information['name'])
7 changes: 4 additions & 3 deletions hpOneView/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ def encode_multipart_formdata(self, fields, files, baseName, verbose=False):
fin.close()
return content_type

def post_multipart_with_response_handling(self, uri, file_path, baseName):
resp, body = self.post_multipart(uri, None, file_path, baseName)
def post_multipart_with_response_handling(self, uri, file_path, baseName, custom_headers):
resp, body = self.post_multipart(uri, None, file_path, baseName, custom_headers)

if resp.status == 202:
task = self.__get_task_from_response(resp, body)
Expand All @@ -283,7 +283,7 @@ def post_multipart_with_response_handling(self, uri, file_path, baseName):

return None, body

def post_multipart(self, uri, fields, files, baseName, verbose=False):
def post_multipart(self, uri, fields, files, baseName, custom_headers, verbose=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again custom_headers should be added in the end of args list as we discussed and is custom_headers a mandatory param. If not, then better to give default value

content_type = self.encode_multipart_formdata(fields, files, baseName,
verbose)
inputfile = self._open(files + '.b64', 'rb')
Expand All @@ -295,6 +295,7 @@ def post_multipart(self, uri, fields, files, baseName, verbose=False):
conn.connect()
conn.putrequest('POST', uri)
conn.putheader('uploadfilename', baseName)
conn.putheader('initialScopeUris', custom_headers['initialScopeUris'])
conn.putheader('auth', self._headers['auth'])
conn.putheader('Content-Type', content_type)
totalSize = os.path.getsize(files + '.b64')
Expand Down
4 changes: 1 addition & 3 deletions hpOneView/oneview_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -905,9 +905,7 @@ def firmware_bundles(self):
Returns:
FirmwareBundles:
"""
if not self.__firmware_bundles:
self.__firmware_bundles = FirmwareBundles(self.__connection)
return self.__firmware_bundles
return FirmwareBundles(self.__connection)

@property
def uplink_sets(self):
Expand Down
4 changes: 2 additions & 2 deletions hpOneView/resources/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ def create(self, resource, uri=None, timeout=-1, custom_headers=None, default_va

return self.__do_post(uri, resource, timeout, custom_headers)

def upload(self, file_path, uri=None, timeout=-1):
def upload(self, file_path, uri=None, timeout=-1, custom_headers=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add documentation for the new arg in the comments section

"""
Makes a multipart request.

Expand All @@ -1229,7 +1229,7 @@ def upload(self, file_path, uri=None, timeout=-1):
uri = self._uri

upload_file_name = os.path.basename(file_path)
task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name)
task, entity = self._connection.post_multipart_with_response_handling(uri, file_path, upload_file_name, custom_headers)

if not task:
return entity
Expand Down
5 changes: 3 additions & 2 deletions hpOneView/resources/settings/firmware_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self, con):
self._connection = con
self._client = ResourceClient(con, self.URI)

def upload(self, file_path, timeout=-1):
def upload(self, file_path, timeout=-1, custom_headers=None,):
Copy link
Member

@soodpr soodpr Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma in the end of args list and add documentation

"""
Upload an SPP ISO image file or a hotfix file to the appliance.
The API supports upload of one hotfix at a time into the system.
Expand All @@ -59,4 +59,5 @@ def upload(self, file_path, timeout=-1):
Returns:
dict: Information about the updated firmware bundle.
"""
return self._client.upload(file_path, timeout=timeout)
# custom_headers = { 'initialScopeUris': '/rest/scopes/bf3e77e3-3248-41b3-aaee-5d83b6ac4b49'}
return self._client.upload(file_path, timeout=timeout, custom_headers=custom_headers)
2 changes: 1 addition & 1 deletion tests/unit/resources/settings/test_firmware_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ def test_upload(self, mock_upload):

self._firmware_bundles.upload(firmware_path)

mock_upload.assert_called_once_with(firmware_path, timeout=-1)
mock_upload.assert_called_once_with(firmware_path, timeout=-1, custom_headers=None)
4 changes: 2 additions & 2 deletions tests/unit/resources/test_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -1217,7 +1217,7 @@ def test_upload_should_call_post_multipart(self, mock_post_multipart):

self.resource_client.upload(filepath, uri)

mock_post_multipart.assert_called_once_with(uri, filepath, 'SPPgen9snap6.2015_0405.81.iso')
mock_post_multipart.assert_called_once_with(uri, filepath, 'SPPgen9snap6.2015_0405.81.iso', None)

@mock.patch.object(connection, 'post_multipart_with_response_handling')
def test_upload_should_call_post_multipart_with_resource_uri_when_not_uri_provided(self, mock_post_multipart):
Expand All @@ -1226,7 +1226,7 @@ def test_upload_should_call_post_multipart_with_resource_uri_when_not_uri_provid

self.resource_client.upload(filepath)

mock_post_multipart.assert_called_once_with('/rest/testuri', mock.ANY, mock.ANY)
mock_post_multipart.assert_called_once_with('/rest/testuri', mock.ANY, mock.ANY, mock.ANY)

@mock.patch.object(connection, 'post_multipart_with_response_handling')
@mock.patch.object(TaskMonitor, 'wait_for_task')
Expand Down
17 changes: 13 additions & 4 deletions tests/unit/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ def test_post_multipart_should_put_request(self, mock_rm, mock_path_size, mock_c
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

internal_conn = self.connection.get_connection.return_value
Expand All @@ -646,10 +647,12 @@ def test_post_multipart_should_put_headers(self, mock_rm, mock_path_size, mock_c
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

expected_putheader_calls = [
call('uploadfilename', 'archive.zip'),
call(u'initialScopeUris', '/rest/scopes/fake'),
call('auth', 'LTIxNjUzMjc0OTUzzHoF7eEkZLEUWVA-fuOZP4VGA3U8e67E'),
call('Content-Type', 'multipart/form-data; boundary=----------ThIs_Is_tHe_bouNdaRY_$'),
call('Content-Length', 2621440),
Expand All @@ -669,6 +672,7 @@ def test_post_multipart_should_read_file_in_chunks_of_1mb(self, mock_rm, mock_pa
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

expected_mmap_read_calls = [
Expand All @@ -689,6 +693,7 @@ def test_post_multipart_should_send_file_in_chuncks_of_1mb(self, mock_rm, mock_p
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

expected_conn_send_calls = [
Expand All @@ -710,6 +715,7 @@ def test_post_multipart_should_remove_temp_encoded_file(self, mock_rm, mock_path
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

mock_rm.assert_called_once_with('/a/path/filename.zip.b64')
Expand All @@ -727,6 +733,7 @@ def test_post_multipart_should_raise_exception_when_response_status_400(self, mo
self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")
except HPOneViewException as e:
self.assertEqual(e.msg, "An error occurred.")
Expand All @@ -745,6 +752,7 @@ def test_post_multipart_should_return_response_and_body_when_response_status_200
response, body = self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

self.assertEqual(body, self.expected_response_body)
Expand All @@ -764,6 +772,7 @@ def test_post_multipart_should_handle_json_load_exception(self, mock_json_loads,
response, body = self.connection.post_multipart(uri='/rest/resources/',
fields=None,
files="/a/path/filename.zip",
custom_headers={'initialScopeUris': '/rest/scopes/fake'},
baseName="archive.zip")

self.assertTrue(body)
Expand All @@ -775,7 +784,7 @@ def test_post_multipart_with_response_handling_when_status_202_without_task(self
mock_response.getheader.return_value = None
mock_post_multipart.return_value = mock_response, "content"

task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "basename")
task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "custom_headers", "basename")

self.assertFalse(task)
self.assertEqual(body, "content")
Expand All @@ -789,7 +798,7 @@ def test_post_multipart_with_response_handling_when_status_202_with_task(self, m
mock_post_multipart.return_value = mock_response, "content"
mock_get.return_value = fake_task

task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "basename")
task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "custom_headers", "basename")

self.assertEqual(task, fake_task)
self.assertEqual(body, "content")
Expand All @@ -799,7 +808,7 @@ def test_post_multipart_with_response_handling_when_status_200_and_body_is_task(
fake_task = {"category": "tasks"}
mock_post_multipart.return_value = Mock(status=200), fake_task

task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "basename")
task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "custom_headers", "basename")

self.assertEqual(task, fake_task)
self.assertEqual(body, fake_task)
Expand All @@ -808,7 +817,7 @@ def test_post_multipart_with_response_handling_when_status_200_and_body_is_task(
def test_post_multipart_with_response_handling_when_status_200_and_body_is_not_task(self, mock_post_multipart):
mock_post_multipart.return_value = Mock(status=200), "content"

task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "basename")
task, body = self.connection.post_multipart_with_response_handling("uri", "filepath", "custom_headers", "basename")

self.assertFalse(task)
self.assertEqual(body, "content")
Expand Down
9 changes: 6 additions & 3 deletions tests/unit/test_oneview_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
from hpOneView.resources.settings.appliance_node_information import ApplianceNodeInformation
from hpOneView.resources.settings.appliance_time_and_locale_configuration import ApplianceTimeAndLocaleConfiguration
from hpOneView.resources.settings.versions import Versions
from hpOneView.resources.settings.firmware_bundles import FirmwareBundles
from tests.test_utils import mock_builtin
from hpOneView.resources.settings.licenses import Licenses

Expand Down Expand Up @@ -566,9 +567,11 @@ def test_lazy_loading_firmware_drivers(self):
firmware_drivers = self._oneview.firmware_drivers
self.assertEqual(firmware_drivers, self._oneview.firmware_drivers)

def test_lazy_loading_firmware_bundles(self):
firmware_bundles = self._oneview.firmware_bundles
self.assertEqual(firmware_bundles, self._oneview.firmware_bundles)
def test_firmware_bundles_has_right_type(self):
self.assertIsInstance(self._oneview.firmware_bundles, FirmwareBundles)

def test_firmware_bundles_has_value(self):
self.assertIsNotNone(self._oneview.firmware_bundles)

def test_migratable_vc_domains_has_right_type(self):
self.assertIsInstance(self._oneview.migratable_vc_domains, MigratableVcDomains)
Expand Down