From 0b1f63778bdb02dd7f956be00dc9986e805faa2c Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Wed, 3 Oct 2018 14:16:38 -0500 Subject: [PATCH 1/7] Add export/import capabilities to/from IBM Cloud Object Storage Change image manager to include IBM Cloud Object Storage support and add unit tests for it. --- ...rtual_Guest_Block_Device_Template_Group.py | 8 ++- SoftLayer/managers/image.py | 60 +++++++++++++++---- tests/managers/image_tests.py | 41 +++++++++++++ 3 files changed, 98 insertions(+), 11 deletions(-) diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py b/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py index ee58ad762..785ed3b05 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py @@ -29,5 +29,11 @@ 'id': 100, 'name': 'test_image', }] - +createFromIcos = [{ + 'createDate': '2013-12-05T21:53:03-06:00', + 'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', + 'id': 100, + 'name': 'test_image', +}] copyToExternalSource = True +copyToIcos = True diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py index 81eee9282..7ebf1fd98 100644 --- a/SoftLayer/managers/image.py +++ b/SoftLayer/managers/image.py @@ -120,28 +120,68 @@ def edit(self, image_id, name=None, note=None, tag=None): return bool(name or note or tag) - def import_image_from_uri(self, name, uri, os_code=None, note=None): + def import_image_from_uri(self, name, uri, os_code=None, note=None, + ibm_api_key=None, root_key_id=None, + wrapped_dek=None, kp_id=None, cloud_init=None, + byol=None, is_encrypted=None): """Import a new image from object storage. :param string name: Name of the new image :param string uri: The URI for an object storage object (.vhd/.iso file) of the format: swift://@// + or (.vhd/.iso/.raw file) of the format: + cos://// if using IBM Cloud + Object Storage :param string os_code: The reference code of the operating system :param string note: Note to add to the image + :param string ibm_api_key: Ibm Api Key needed to communicate with ICOS + and Key Protect + :param string root_key_id: ID of the root key in Key Protect + :param string wrapped_dek: Wrapped Decryption Key provided by IBM + KeyProtect + :param string kp_id: ID of the IBM Key Protect Instance + :param bool cloud_init: Specifies if image is cloud init + :param bool byol: Specifies if image is bring your own license + :param bool is_encrypted: Specifies if image is encrypted """ - return self.vgbdtg.createFromExternalSource({ - 'name': name, - 'note': note, - 'operatingSystemReferenceCode': os_code, - 'uri': uri, - }) - - def export_image_to_uri(self, image_id, uri): + if 'cos://' in uri: + return self.vgbdtg.createFromIcos({ + 'name': name, + 'note': note, + 'operatingSystemReferenceCode': os_code, + 'uri': uri, + 'ibmApiKey': ibm_api_key, + 'rootKeyid': root_key_id, + 'wrappedDek': wrapped_dek, + 'keyProtectId': kp_id, + 'cloudInit': cloud_init, + 'byol': byol, + 'isEncrypted': is_encrypted + }) + else: + return self.vgbdtg.createFromExternalSource({ + 'name': name, + 'note': note, + 'operatingSystemReferenceCode': os_code, + 'uri': uri, + }) + + def export_image_to_uri(self, image_id, uri, ibm_api_key=None): """Export image into the given object storage :param int image_id: The ID of the image :param string uri: The URI for object storage of the format swift://@// + or cos://// if using IBM Cloud + Object Storage + :param string ibm_api_key: Ibm Api Key needed to communicate with IBM + Cloud Object Storage """ - return self.vgbdtg.copyToExternalSource({'uri': uri}, id=image_id) + if 'cos://' in uri: + return self.vgbdtg.copyToIcos({ + 'uri': uri, + 'ibmApiKey': ibm_api_key + }, id=image_id) + else: + return self.vgbdtg.copyToExternalSource({'uri': uri}, id=image_id) diff --git a/tests/managers/image_tests.py b/tests/managers/image_tests.py index 5347d4e71..ba410b646 100644 --- a/tests/managers/image_tests.py +++ b/tests/managers/image_tests.py @@ -145,6 +145,36 @@ def test_import_image(self): 'uri': 'someuri', 'operatingSystemReferenceCode': 'UBUNTU_LATEST'},)) + def test_import_image_cos(self): + self.image.import_image_from_uri(name='test_image', + note='testimage', + uri='cos://some_uri', + os_code='UBUNTU_LATEST', + ibm_api_key='some_ibm_key', + root_key_id='some_root_key_id', + wrapped_dek='some_dek', + kp_id='some_id', + cloud_init=False, + byol=False, + is_encrypted=False + ) + + self.assert_called_with( + IMAGE_SERVICE, + 'createFromIcos', + args=({'name': 'test_image', + 'note': 'testimage', + 'operatingSystemReferenceCode': 'UBUNTU_LATEST', + 'uri': 'cos://some_uri', + 'ibmApiKey': 'some_ibm_key', + 'rootKeyid': 'some_root_key_id', + 'wrappedDek': 'some_dek', + 'keyProtectId': 'some_id', + 'cloudInit': False, + 'byol': False, + 'isEncrypted': False + },)) + def test_export_image(self): self.image.export_image_to_uri(1234, 'someuri') @@ -153,3 +183,14 @@ def test_export_image(self): 'copyToExternalSource', args=({'uri': 'someuri'},), identifier=1234) + + def test_export_image_cos(self): + self.image.export_image_to_uri(1234, + 'cos://someuri', + ibm_api_key='someApiKey') + + self.assert_called_with( + IMAGE_SERVICE, + 'copyToIcos', + args=({'uri': 'cos://someuri', 'ibmApiKey': 'someApiKey'},), + identifier=1234) From df0f47f62fb4aefeab9f1868a1547db330ea110d Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Thu, 4 Oct 2018 14:41:54 -0500 Subject: [PATCH 2/7] Fix manager and add CLI support --- SoftLayer/CLI/image/export.py | 10 ++++++++-- SoftLayer/CLI/image/import.py | 36 +++++++++++++++++++++++++++++++++-- SoftLayer/managers/image.py | 8 ++++---- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/SoftLayer/CLI/image/export.py b/SoftLayer/CLI/image/export.py index 327cef475..2dd5f4568 100644 --- a/SoftLayer/CLI/image/export.py +++ b/SoftLayer/CLI/image/export.py @@ -12,17 +12,23 @@ @click.command() @click.argument('identifier') @click.argument('uri') +@click.option('--ibm_api_key', + default="", + help="The IBM Cloud API Key with access to IBM Cloud Object " + "Storage instance.") @environment.pass_env -def cli(env, identifier, uri): +def cli(env, identifier, uri, ibm_api_key): """Export an image to object storage. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// + or cos://// if using IBM Cloud + Object Storage """ image_mgr = SoftLayer.ImageManager(env.client) image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') - result = image_mgr.export_image_to_uri(image_id, uri) + result = image_mgr.export_image_to_uri(image_id, uri, ibm_api_key) if not result: raise exceptions.CLIAbort("Failed to export Image") diff --git a/SoftLayer/CLI/image/import.py b/SoftLayer/CLI/image/import.py index 03ec25acb..bd19b5ca7 100644 --- a/SoftLayer/CLI/image/import.py +++ b/SoftLayer/CLI/image/import.py @@ -18,13 +18,38 @@ @click.option('--os-code', default="", help="The referenceCode of the operating system software" - " description for the imported VHD") + " description for the imported VHD, ISO, or RAW image") +@click.option('--ibm-api-key', + default="", + help="The IBM Cloud API Key with access to IBM Cloud Object " + "Storage instance.") +@click.option('--root-key-id', + default="", + help="ID of the root key in Key Protect") +@click.option('--wrapped-dek', + default="", + help="Wrapped Decryption Key provided by IBM KeyProtect") +@click.option('--kp-id', + default="", + help="ID of the IBM Key Protect Instance") +@click.option('--cloud-init', + default="", + help="Specifies if image is cloud init") +@click.option('--byol', + default="", + help="Specifies if image is bring your own license") +@click.option('--is-encrypted', + default="", + help="Specifies if image is encrypted") @environment.pass_env -def cli(env, name, note, os_code, uri): +def cli(env, name, note, os_code, uri, ibm_api_key, root_key_id, wrapped_dek, + kp_id, cloud_init, byol, is_encrypted): """Import an image. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// + or cos://// if using IBM Cloud + Object Storage """ image_mgr = SoftLayer.ImageManager(env.client) @@ -33,6 +58,13 @@ def cli(env, name, note, os_code, uri): note=note, os_code=os_code, uri=uri, + ibm_api_key=ibm_api_key, + root_key_id=root_key_id, + wrapped_dek=wrapped_dek, + kp_id=kp_id, + cloud_init=cloud_init, + byol=byol, + is_encrypted=is_encrypted ) if not result: diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py index 7ebf1fd98..c11c0a890 100644 --- a/SoftLayer/managers/image.py +++ b/SoftLayer/managers/image.py @@ -141,9 +141,9 @@ def import_image_from_uri(self, name, uri, os_code=None, note=None, :param string wrapped_dek: Wrapped Decryption Key provided by IBM KeyProtect :param string kp_id: ID of the IBM Key Protect Instance - :param bool cloud_init: Specifies if image is cloud init - :param bool byol: Specifies if image is bring your own license - :param bool is_encrypted: Specifies if image is encrypted + :param boolean cloud_init: Specifies if image is cloud init + :param boolean byol: Specifies if image is bring your own license + :param boolean is_encrypted: Specifies if image is encrypted """ if 'cos://' in uri: return self.vgbdtg.createFromIcos({ @@ -152,7 +152,7 @@ def import_image_from_uri(self, name, uri, os_code=None, note=None, 'operatingSystemReferenceCode': os_code, 'uri': uri, 'ibmApiKey': ibm_api_key, - 'rootKeyid': root_key_id, + 'rootKeyId': root_key_id, 'wrappedDek': wrapped_dek, 'keyProtectId': kp_id, 'cloudInit': cloud_init, From f4b797dce67c9ecfa3cbaf99f3d765ac3d4d4002 Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Thu, 4 Oct 2018 16:37:08 -0500 Subject: [PATCH 3/7] Fix test --- tests/managers/image_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/managers/image_tests.py b/tests/managers/image_tests.py index ba410b646..50a081988 100644 --- a/tests/managers/image_tests.py +++ b/tests/managers/image_tests.py @@ -167,7 +167,7 @@ def test_import_image_cos(self): 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'uri': 'cos://some_uri', 'ibmApiKey': 'some_ibm_key', - 'rootKeyid': 'some_root_key_id', + 'rootKeyId': 'some_root_key_id', 'wrappedDek': 'some_dek', 'keyProtectId': 'some_id', 'cloudInit': False, From a0da453e4fcca1ac4a21089374c43a041e2b7efe Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Fri, 5 Oct 2018 15:47:55 -0500 Subject: [PATCH 4/7] Fixed name of ibm-api-key in cli --- SoftLayer/CLI/image/export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SoftLayer/CLI/image/export.py b/SoftLayer/CLI/image/export.py index 2dd5f4568..76ef5f164 100644 --- a/SoftLayer/CLI/image/export.py +++ b/SoftLayer/CLI/image/export.py @@ -12,7 +12,7 @@ @click.command() @click.argument('identifier') @click.argument('uri') -@click.option('--ibm_api_key', +@click.option('--ibm-api-key', default="", help="The IBM Cloud API Key with access to IBM Cloud Object " "Storage instance.") From d4a72b32073d0d9b0fbc7bae413071106ca9d668 Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Mon, 8 Oct 2018 14:42:25 -0500 Subject: [PATCH 5/7] Address comments and add appropriate code --- SoftLayer/CLI/image/export.py | 4 +++- SoftLayer/CLI/image/import.py | 16 ++++++++++------ SoftLayer/managers/image.py | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/SoftLayer/CLI/image/export.py b/SoftLayer/CLI/image/export.py index 76ef5f164..fec494e5f 100644 --- a/SoftLayer/CLI/image/export.py +++ b/SoftLayer/CLI/image/export.py @@ -15,7 +15,9 @@ @click.option('--ibm-api-key', default="", help="The IBM Cloud API Key with access to IBM Cloud Object " - "Storage instance.") + "Storage instance. For help creating this key see " + "https://console.bluemix.net/docs/services/cloud-object-" + "storage/iam/users-serviceids.html#serviceidapikeys") @environment.pass_env def cli(env, identifier, uri, ibm_api_key): """Export an image to object storage. diff --git a/SoftLayer/CLI/image/import.py b/SoftLayer/CLI/image/import.py index bd19b5ca7..a0e65030c 100644 --- a/SoftLayer/CLI/image/import.py +++ b/SoftLayer/CLI/image/import.py @@ -22,24 +22,28 @@ @click.option('--ibm-api-key', default="", help="The IBM Cloud API Key with access to IBM Cloud Object " - "Storage instance.") + "Storage instance. For help creating this key see " + "https://console.bluemix.net/docs/services/cloud-object-" + "storage/iam/users-serviceids.html#serviceidapikeys") @click.option('--root-key-id', default="", help="ID of the root key in Key Protect") @click.option('--wrapped-dek', default="", - help="Wrapped Decryption Key provided by IBM KeyProtect") + help="Wrapped Data Encryption Key provided by IBM KeyProtect. " + "For more info see https://console.bluemix.net/docs/" + "services/key-protect/wrap-keys.html#wrap-keys") @click.option('--kp-id', default="", help="ID of the IBM Key Protect Instance") @click.option('--cloud-init', - default="", - help="Specifies if image is cloud init") + is_flag=True, + help="Specifies if image is cloud-init") @click.option('--byol', - default="", + is_flag=True, help="Specifies if image is bring your own license") @click.option('--is-encrypted', - default="", + is_flag=True, help="Specifies if image is encrypted") @environment.pass_env def cli(env, name, note, os_code, uri, ibm_api_key, root_key_id, wrapped_dek, diff --git a/SoftLayer/managers/image.py b/SoftLayer/managers/image.py index c11c0a890..abd60ba8a 100644 --- a/SoftLayer/managers/image.py +++ b/SoftLayer/managers/image.py @@ -122,8 +122,8 @@ def edit(self, image_id, name=None, note=None, tag=None): def import_image_from_uri(self, name, uri, os_code=None, note=None, ibm_api_key=None, root_key_id=None, - wrapped_dek=None, kp_id=None, cloud_init=None, - byol=None, is_encrypted=None): + wrapped_dek=None, kp_id=None, cloud_init=False, + byol=False, is_encrypted=False): """Import a new image from object storage. :param string name: Name of the new image @@ -138,10 +138,10 @@ def import_image_from_uri(self, name, uri, os_code=None, note=None, :param string ibm_api_key: Ibm Api Key needed to communicate with ICOS and Key Protect :param string root_key_id: ID of the root key in Key Protect - :param string wrapped_dek: Wrapped Decryption Key provided by IBM - KeyProtect + :param string wrapped_dek: Wrapped Data Encryption Key provided by + IBM KeyProtect :param string kp_id: ID of the IBM Key Protect Instance - :param boolean cloud_init: Specifies if image is cloud init + :param boolean cloud_init: Specifies if image is cloud-init :param boolean byol: Specifies if image is bring your own license :param boolean is_encrypted: Specifies if image is encrypted """ From 2385bf24c8a06812ebcc64f38937ce7203f5b987 Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Mon, 8 Oct 2018 14:47:23 -0500 Subject: [PATCH 6/7] Add KeyProtect instance in help text --- SoftLayer/CLI/image/import.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SoftLayer/CLI/image/import.py b/SoftLayer/CLI/image/import.py index a0e65030c..1eb7e1658 100644 --- a/SoftLayer/CLI/image/import.py +++ b/SoftLayer/CLI/image/import.py @@ -22,9 +22,10 @@ @click.option('--ibm-api-key', default="", help="The IBM Cloud API Key with access to IBM Cloud Object " - "Storage instance. For help creating this key see " - "https://console.bluemix.net/docs/services/cloud-object-" - "storage/iam/users-serviceids.html#serviceidapikeys") + "Storage instance and IBM KeyProtect instance. For help " + "creating this key see https://console.bluemix.net/docs/" + "services/cloud-object-storage/iam/users-serviceids.html" + "#serviceidapikeys") @click.option('--root-key-id', default="", help="ID of the root key in Key Protect") From 3a6b7b30b286fef55f713b49ab5a2ffc2d7a90bc Mon Sep 17 00:00:00 2001 From: Michael Wurtz Date: Tue, 9 Oct 2018 13:35:56 -0500 Subject: [PATCH 7/7] Change defaults to None --- SoftLayer/CLI/image/export.py | 2 +- SoftLayer/CLI/image/import.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/SoftLayer/CLI/image/export.py b/SoftLayer/CLI/image/export.py index fec494e5f..375de7842 100644 --- a/SoftLayer/CLI/image/export.py +++ b/SoftLayer/CLI/image/export.py @@ -13,7 +13,7 @@ @click.argument('identifier') @click.argument('uri') @click.option('--ibm-api-key', - default="", + default=None, help="The IBM Cloud API Key with access to IBM Cloud Object " "Storage instance. For help creating this key see " "https://console.bluemix.net/docs/services/cloud-object-" diff --git a/SoftLayer/CLI/image/import.py b/SoftLayer/CLI/image/import.py index 1eb7e1658..525564416 100644 --- a/SoftLayer/CLI/image/import.py +++ b/SoftLayer/CLI/image/import.py @@ -16,26 +16,25 @@ default="", help="The note to be applied to the imported template") @click.option('--os-code', - default="", help="The referenceCode of the operating system software" " description for the imported VHD, ISO, or RAW image") @click.option('--ibm-api-key', - default="", + default=None, help="The IBM Cloud API Key with access to IBM Cloud Object " "Storage instance and IBM KeyProtect instance. For help " "creating this key see https://console.bluemix.net/docs/" "services/cloud-object-storage/iam/users-serviceids.html" "#serviceidapikeys") @click.option('--root-key-id', - default="", + default=None, help="ID of the root key in Key Protect") @click.option('--wrapped-dek', - default="", + default=None, help="Wrapped Data Encryption Key provided by IBM KeyProtect. " "For more info see https://console.bluemix.net/docs/" "services/key-protect/wrap-keys.html#wrap-keys") @click.option('--kp-id', - default="", + default=None, help="ID of the IBM Key Protect Instance") @click.option('--cloud-init', is_flag=True,