Repository: softlayer/softlayer-python
Pull request #1025 (2018-08-23 18:17:10)

create dedicated host with gpu
Create dedicated host with gpu https://github.com/softlayer/softlayer-python/issues/1022.


Patch:
diff --git a/SoftLayer/API.py b/SoftLayer/API.py
index c5fd95f3..3fbab72b 100644
--- a/SoftLayer/API.py
+++ b/SoftLayer/API.py
@@ -298,7 +298,7 @@ def iter_call(self, service, method, *args, **kwargs):
                     results = transports.SoftLayerListResult(results, len(results))
                 else:
                     yield results
-                    return
+                    raise StopIteration
 
             for item in results:
                 yield item
@@ -313,6 +313,8 @@ def iter_call(self, service, method, *args, **kwargs):
 
             offset += limit
 
+        raise StopIteration
+
     def __repr__(self):
         return "Client(transport=%r, auth=%r)" % (self.transport, self.auth)
 
diff --git a/SoftLayer/CLI/hardware/credentials.py b/SoftLayer/CLI/hardware/credentials.py
index 877d5e9b..3b1c0798 100644
--- a/SoftLayer/CLI/hardware/credentials.py
+++ b/SoftLayer/CLI/hardware/credentials.py
@@ -23,9 +23,9 @@ def cli(env, identifier):
     instance = manager.get_hardware(hardware_id)
 
     table = formatting.Table(['username', 'password'])
-    for item in instance['softwareComponents']:
-        if 'passwords' not in item:
-            raise exceptions.SoftLayerError("No passwords found in softwareComponents")
-        for credentials in item['passwords']:
-            table.add_row([credentials.get('username', 'None'), credentials.get('password', 'None')])
+    if 'passwords' not in instance['operatingSystem']:
+        raise exceptions.SoftLayerError("No passwords found in operatingSystem")
+
+    for item in instance['operatingSystem']['passwords']:
+        table.add_row([item.get('username', 'None'), item.get('password', 'None')])
     env.fout(table)
diff --git a/SoftLayer/fixtures/SoftLayer_Product_Package.py b/SoftLayer/fixtures/SoftLayer_Product_Package.py
index deef5825..9b5d5374 100644
--- a/SoftLayer/fixtures/SoftLayer_Product_Package.py
+++ b/SoftLayer/fixtures/SoftLayer_Product_Package.py
@@ -666,7 +666,6 @@
     ]
 }
 
-
 SAAS_REST_PACKAGE = {
     'categories': [
         {'categoryCode': 'storage_as_a_service'}
@@ -1133,12 +1132,14 @@
         "bundleItems": [
             {
                 "capacity": "1200",
+                "keyName": "1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY",
                 "categories": [{
                     "categoryCode": "dedicated_host_disk"
                 }]
             },
             {
                 "capacity": "242",
+                "keyName": "242_GB_RAM",
                 "categories": [{
                     "categoryCode": "dedicated_host_ram"
                 }]
@@ -1218,6 +1219,110 @@
     "description": "Dedicated Host"
 }]
 
+getAllObjectsDHGpu = [{
+    "subDescription": "Dedicated Host",
+    "name": "Dedicated Host",
+    "items": [{
+        "capacity": "56",
+        "description": "56 Cores x 360 RAM x 1.2 TB x 2 GPU P100 [encryption enabled]",
+        "bundleItems": [
+            {
+                "capacity": "1200",
+                "keyName": "1.2 TB Local Storage (Dedicated Host Capacity)",
+                "categories": [{
+                    "categoryCode": "dedicated_host_disk"
+                }]
+            },
+            {
+                "capacity": "242",
+                "keyName": "2_GPU_P100_DEDICATED",
+                "hardwareGenericComponentModel": {
+                    "capacity": "16",
+                    "id": 849,
+                    "hardwareComponentType": {
+                        "id": 20,
+                        "keyName": "GPU"
+                    }
+                },
+                "categories": [{
+                    "categoryCode": "dedicated_host_ram"
+                }]
+            }
+        ],
+        "prices": [
+            {
+                "itemId": 10195,
+                "setupFee": "0",
+                "recurringFee": "2099",
+                "tierMinimumThreshold": "",
+                "hourlyRecurringFee": "3.164",
+                "oneTimeFee": "0",
+                "currentPriceFlag": "",
+                "id": 200269,
+                "sort": 0,
+                "onSaleFlag": "",
+                "laborFee": "0",
+                "locationGroupId": "",
+                "quantity": ""
+            },
+            {
+                "itemId": 10195,
+                "setupFee": "0",
+                "recurringFee": "2161.97",
+                "tierMinimumThreshold": "",
+                "hourlyRecurringFee": "3.258",
+                "oneTimeFee": "0",
+                "currentPriceFlag": "",
+                "id": 200271,
+                "sort": 0,
+                "onSaleFlag": "",
+                "laborFee": "0",
+                "locationGroupId": 503,
+                "quantity": ""
+            }
+        ],
+        "keyName": "56_CORES_X_484_RAM_X_1_5_TB_X_2_GPU_P100",
+        "id": 10195,
+        "itemCategory": {
+            "categoryCode": "dedicated_virtual_hosts"
+        }
+    }],
+    "keyName": "DEDICATED_HOST",
+    "unitSize": "",
+    "regions": [{
+        "location": {
+            "locationPackageDetails": [{
+                "isAvailable": 1,
+                "locationId": 138124,
+                "packageId": 813
+            }],
+            "location": {
+                "statusId": 2,
+                "priceGroups": [{
+                    "locationGroupTypeId": 82,
+                    "description": "CDN - North America - Akamai",
+                    "locationGroupType": {
+                        "name": "PRICING"
+                    },
+                    "securityLevelId": "",
+                    "id": 1463,
+                    "name": "NORTH-AMERICA-AKAMAI"
+                }],
+                "id": 138124,
+                "name": "dal05",
+                "longName": "Dallas 5"
+            }
+        },
+        "keyname": "DALLAS05",
+        "description": "DAL05 - Dallas",
+        "sortOrder": 12
+    }],
+    "firstOrderStepId": "",
+    "id": 813,
+    "isActive": 1,
+    "description": "Dedicated Host"
+}]
+
 getRegions = [{
     "description": "WDC07 - Washington, DC",
     "keyname": "WASHINGTON07",
diff --git a/SoftLayer/managers/dedicated_host.py b/SoftLayer/managers/dedicated_host.py
index 6f6fe596..6d3b6bb9 100644
--- a/SoftLayer/managers/dedicated_host.py
+++ b/SoftLayer/managers/dedicated_host.py
@@ -243,7 +243,8 @@ def _get_package(self):
             capacity,
             keyName,
             itemCategory[categoryCode],
-            bundleItems[capacity, categories[categoryCode]]
+            bundleItems[capacity,keyName,categories[categoryCode],hardwareGenericComponentModel[id,
+            hardwareComponentType[keyName]]]
         ],
         regions[location[location[priceGroups]]]
         '''
@@ -317,18 +318,46 @@ def _get_backend_router(self, locations, item):
                 if category['categoryCode'] == 'dedicated_host_disk':
                     disk_capacity = capacity['capacity']
 
+        for hardwareComponent in item['bundleItems']:
+            if hardwareComponent['keyName'].find("GPU") != -1:
+                hardwareComponentType = hardwareComponent['hardwareGenericComponentModel']['hardwareComponentType']
+                gpuComponents = [
+                    {
+                        'hardwareComponentModel': {
+                            'hardwareGenericComponentModel': {
+                                'id': hardwareComponent['hardwareGenericComponentModel']['id'],
+                                'hardwareComponentType': {
+                                    'keyName': hardwareComponentType['keyName']
+                                }
+                            }
+                        }
+                    },
+                    {
+                        'hardwareComponentModel': {
+                            'hardwareGenericComponentModel': {
+                                'id': hardwareComponent['hardwareGenericComponentModel']['id'],
+                                'hardwareComponentType': {
+                                    'keyName': hardwareComponentType['keyName']
+                                }
+                            }
+                        }
+                    }
+                ]
+
         if locations is not None:
             for location in locations:
                 if location['locationId'] is not None:
                     loc_id = location['locationId']
                     host = {
-                        'cpuCount': cpu_count,
-                        'memoryCapacity': mem_capacity,
-                        'diskCapacity': disk_capacity,
-                        'datacenter': {
-                            'id': loc_id
+                            'cpuCount': cpu_count,
+                            'memoryCapacity': mem_capacity,
+                            'diskCapacity': disk_capacity,
+                            'datacenter': {
+                                'id': loc_id
+                            }
                         }
-                    }
+                    if item['keyName'].find("GPU") != -1:
+                        host['pciDevices'] = gpuComponents
                     routers = self.host.getAvailableRouters(host, mask=mask)
                     return routers
 
diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py
index c105b3b5..7e0c3377 100644
--- a/SoftLayer/managers/hardware.py
+++ b/SoftLayer/managers/hardware.py
@@ -245,12 +245,6 @@ def get_hardware(self, hardware_id, **kwargs):
                                                         version,
                                                         referenceCode]],
                     passwords[username,password]],'''
-                '''softwareComponents[
-                    softwareLicense[softwareDescription[manufacturer,
-                                                        name,
-                                                        version,
-                                                        referenceCode]],
-                    passwords[username,password]],'''
                 'billingItem['
                 'id,nextInvoiceTotalRecurringAmount,'
                 'children[nextInvoiceTotalRecurringAmount],'


Test patch:
diff --git a/tests/CLI/modules/dedicatedhost_tests.py b/tests/CLI/modules/dedicatedhost_tests.py
index ead83526..59f6cab7 100644
--- a/tests/CLI/modules/dedicatedhost_tests.py
+++ b/tests/CLI/modules/dedicatedhost_tests.py
@@ -85,10 +85,10 @@ def test_details_no_owner(self):
                                                      'disk capacity': 1200,
                                                      'guest count': 1,
                                                      'guests': [{
-                                                                    'domain': 'Softlayer.com',
-                                                                    'hostname': 'khnguyenDHI',
-                                                                    'id': 43546081,
-                                                                    'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2'}],
+                                                         'domain': 'Softlayer.com',
+                                                         'hostname': 'khnguyenDHI',
+                                                         'id': 43546081,
+                                                         'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2'}],
                                                      'id': 44701,
                                                      'memory capacity': 242,
                                                      'modify date': '2017-11-06T11:38:20-06:00',
@@ -116,8 +116,8 @@ def test_create_options(self):
                     '56 Cores X 242 RAM X 1.2 TB',
                 'value': '56_CORES_X_242_RAM_X_1_4_TB'
             }
-        ]]
-        )
+            ]]
+                         )
 
     def test_create_options_with_only_datacenter(self):
         mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
@@ -137,19 +137,19 @@ def test_create_options_get_routers(self):
         self.assert_no_fail(result)
         self.assertEqual(json.loads(result.output), [[
             {
-                "Available Backend Routers": "bcr01a.dal05"
+                'Available Backend Routers': 'bcr01a.dal05'
             },
             {
-                "Available Backend Routers": "bcr02a.dal05"
+                'Available Backend Routers': 'bcr02a.dal05'
             },
             {
-                "Available Backend Routers": "bcr03a.dal05"
+                'Available Backend Routers': 'bcr03a.dal05'
             },
             {
-                "Available Backend Routers": "bcr04a.dal05"
+                'Available Backend Routers': 'bcr04a.dal05'
             }
         ]]
-        )
+                         )
 
     def test_create(self):
         SoftLayer.CLI.formatting.confirm = mock.Mock()
@@ -166,25 +166,59 @@ def test_create(self):
                                    '--billing=hourly'])
         self.assert_no_fail(result)
         args = ({
-            'hardware': [{
-                'domain': 'example.com',
-                'primaryBackendNetworkComponent': {
-                    'router': {
-                        'id': 51218
-                    }
-                },
-                'hostname': 'host'
-            }],
-            'prices': [{
-                'id': 200269
-            }],
-            'location': 'DALLAS05',
-            'packageId': 813,
-            'complexType':
-            'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
-            'useHourlyPricing': True,
-            'quantity': 1},
-        )
+                    'hardware': [{
+                        'domain': 'example.com',
+                        'primaryBackendNetworkComponent': {
+                            'router': {
+                                'id': 51218
+                            }
+                        },
+                        'hostname': 'host'
+                    }],
+                    'prices': [{
+                        'id': 200269
+                    }],
+                    'location': 'DALLAS05',
+                    'packageId': 813,
+                    'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+                    'useHourlyPricing': True,
+                    'quantity': 1},)
+
+        self.assert_called_with('SoftLayer_Product_Order', 'placeOrder',
+                                args=args)
+
+    def test_create_with_gpu(self):
+        SoftLayer.CLI.formatting.confirm = mock.Mock()
+        SoftLayer.CLI.formatting.confirm.return_value = True
+        mock_package_obj = self.set_mock('SoftLayer_Product_Package',
+                                         'getAllObjects')
+        mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDHGpu
+
+        result = self.run_command(['dedicatedhost', 'create',
+                                   '--hostname=host',
+                                   '--domain=example.com',
+                                   '--datacenter=dal05',
+                                   '--flavor=56_CORES_X_484_RAM_X_1_5_TB_X_2_GPU_P100',
+                                   '--billing=hourly'])
+        self.assert_no_fail(result)
+        args = ({
+                    'hardware': [{
+                        'domain': 'example.com',
+                        'primaryBackendNetworkComponent': {
+                            'router': {
+                                'id': 51218
+                            }
+                        },
+                        'hostname': 'host'
+                    }],
+                    'prices': [{
+                        'id': 200269
+                    }],
+                    'location': 'DALLAS05',
+                    'packageId': 813,
+                    'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+                    'useHourlyPricing': True,
+                    'quantity': 1},)
 
         self.assert_called_with('SoftLayer_Product_Order', 'placeOrder',
                                 args=args)
@@ -207,22 +241,22 @@ def test_create_verify(self):
         self.assert_no_fail(result)
 
         args = ({
-            'useHourlyPricing': True,
-            'hardware': [{
+                    'useHourlyPricing': True,
+                    'hardware': [{
 
-                'hostname': 'host',
-                'domain': 'example.com',
+                        'hostname': 'host',
+                        'domain': 'example.com',
 
-                'primaryBackendNetworkComponent': {
-                    'router': {
-                        'id': 51218
-                    }
-                }
-            }],
-            'packageId': 813, 'prices': [{'id': 200269}],
-            'location': 'DALLAS05',
-            'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
-            'quantity': 1},)
+                        'primaryBackendNetworkComponent': {
+                            'router': {
+                                'id': 51218
+                            }
+                        }
+                    }],
+                    'packageId': 813, 'prices': [{'id': 200269}],
+                    'location': 'DALLAS05',
+                    'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+                    'quantity': 1},)
 
         self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder',
                                 args=args)
@@ -237,20 +271,20 @@ def test_create_verify(self):
         self.assert_no_fail(result)
 
         args = ({
-            'useHourlyPricing': True,
-            'hardware': [{
-                'hostname': 'host',
-                'domain': 'example.com',
-                'primaryBackendNetworkComponent': {
-                    'router': {
-                        'id': 51218
-                    }
-                }
-            }],
-            'packageId': 813, 'prices': [{'id': 200269}],
-            'location': 'DALLAS05',
-            'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
-            'quantity': 1},)
+                    'useHourlyPricing': True,
+                    'hardware': [{
+                        'hostname': 'host',
+                        'domain': 'example.com',
+                        'primaryBackendNetworkComponent': {
+                            'router': {
+                                'id': 51218
+                            }
+                        }
+                    }],
+                    'packageId': 813, 'prices': [{'id': 200269}],
+                    'location': 'DALLAS05',
+                    'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+                    'quantity': 1},)
 
         self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder',
                                 args=args)
@@ -306,22 +340,22 @@ def test_create_verify_no_price_or_more_than_one(self):
 
         self.assertIsInstance(result.exception, exceptions.ArgumentError)
         args = ({
-                'hardware': [{
-                    'domain': 'example.com',
-                    'primaryBackendNetworkComponent': {
-                        'router': {
-                            'id': 51218
-                        }
-                    },
-                    'hostname': 'host'
-                }],
-                'prices': [{
-                    'id': 200269
-                }],
-                'location': 'DALLAS05',
-                'packageId': 813,
-                'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
-                'useHourlyPricing': True,
-                'quantity': 1},)
+                    'hardware': [{
+                        'domain': 'example.com',
+                        'primaryBackendNetworkComponent': {
+                            'router': {
+                                'id': 51218
+                            }
+                        },
+                        'hostname': 'host'
+                    }],
+                    'prices': [{
+                        'id': 200269
+                    }],
+                    'location': 'DALLAS05',
+                    'packageId': 813,
+                    'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+                    'useHourlyPricing': True,
+                    'quantity': 1},)
 
         self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args)
diff --git a/tests/CLI/modules/server_tests.py b/tests/CLI/modules/server_tests.py
index 2f26c4ec..fe42b553 100644
--- a/tests/CLI/modules/server_tests.py
+++ b/tests/CLI/modules/server_tests.py
@@ -27,21 +27,6 @@ def test_server_cancel_reasons(self):
         self.assertEqual(len(output), 10)
 
     def test_server_credentials(self):
-        mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject')
-        mock.return_value = {
-            "accountId": 11111,
-            "domain": "chechu.com",
-            "fullyQualifiedDomainName": "host3.vmware.chechu.com",
-            "hardwareStatusId": 5,
-            "hostname": "host3.vmware",
-            "id": 12345,
-            "softwareComponents": [{"passwords": [
-                {
-                    "password": "abc123",
-                    "username": "root"
-                }
-            ]}]
-        }
         result = self.run_command(['hardware', 'credentials', '12345'])
 
         self.assert_no_fail(result)
@@ -60,13 +45,13 @@ def test_server_credentials_exception_passwords_not_found(self):
             "hardwareStatusId": 5,
             "hostname": "host3.vmware",
             "id": 12345,
-            "softwareComponents": [{}]
+            "operatingSystem": {}
         }
 
         result = self.run_command(['hardware', 'credentials', '12345'])
 
         self.assertEqual(
-            'No passwords found in softwareComponents',
+            'No passwords found in operatingSystem',
             str(result.exception)
         )
 
@@ -79,13 +64,11 @@ def test_server_credentials_exception_password_not_found(self):
             "hardwareStatusId": 5,
             "hostname": "host3.vmware",
             "id": 12345,
-            "softwareComponents": [
-                {
-                    "hardwareId": 22222,
-                    "id": 333333,
-                    "passwords": [{}]
-                }
-            ]
+            "operatingSystem": {
+                "hardwareId": 22222,
+                "id": 333333,
+                "passwords": [{}]
+            }
         }
 
         result = self.run_command(['hardware', 'credentials', '12345'])
diff --git a/tests/managers/dedicated_host_tests.py b/tests/managers/dedicated_host_tests.py
index d6ced130..2f21edac 100644
--- a/tests/managers/dedicated_host_tests.py
+++ b/tests/managers/dedicated_host_tests.py
@@ -133,6 +133,57 @@ def test_place_order(self):
                                 'placeOrder',
                                 args=(values,))
 
+    def test_place_order_with_gpu(self):
+        create_dict = self.dedicated_host._generate_create_dict = mock.Mock()
+
+        values = {
+            'hardware': [
+                {
+                    'primaryBackendNetworkComponent': {
+                        'router': {
+                            'id': 51218
+                        }
+                    },
+                    'domain': u'test.com',
+                    'hostname': u'test'
+                }
+            ],
+            'useHourlyPricing': True,
+            'location': 'AMSTERDAM',
+            'packageId': 813,
+            'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost',
+            'prices': [
+                {
+                    'id': 200269
+                }
+            ],
+            'quantity': 1
+        }
+        create_dict.return_value = values
+
+        location = 'dal05'
+        hostname = 'test'
+        domain = 'test.com'
+        hourly = True
+        flavor = '56_CORES_X_484_RAM_X_1_5_TB_X_2_GPU_P100'
+
+        self.dedicated_host.place_order(hostname=hostname,
+                                        domain=domain,
+                                        location=location,
+                                        flavor=flavor,
+                                        hourly=hourly)
+
+        create_dict.assert_called_once_with(hostname=hostname,
+                                            router=None,
+                                            domain=domain,
+                                            datacenter=location,
+                                            flavor=flavor,
+                                            hourly=True)
+
+        self.assert_called_with('SoftLayer_Product_Order',
+                                'placeOrder',
+                                args=(values,))
+
     def test_verify_order(self):
         create_dict = self.dedicated_host._generate_create_dict = mock.Mock()
 
@@ -286,7 +337,8 @@ def test_get_package(self):
             capacity,
             keyName,
             itemCategory[categoryCode],
-            bundleItems[capacity, categories[categoryCode]]
+            bundleItems[capacity,keyName,categories[categoryCode],hardwareGenericComponentModel[id,
+            hardwareComponentType[keyName]]]
         ],
         regions[location[location[priceGroups]]]
         '''
@@ -388,12 +440,14 @@ def test_get_item(self):
         item = {
             'bundleItems': [{
                 'capacity': '1200',
+                'keyName': '1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY',
                 'categories': [{
                     'categoryCode': 'dedicated_host_disk'
                 }]
             },
                 {
                     'capacity': '242',
+                    'keyName': '242_GB_RAM',
                     'categories': [{
                         'categoryCode': 'dedicated_host_ram'
                     }]
@@ -517,6 +571,7 @@ def _get_package(self):
                     "bundleItems": [
                         {
                             "capacity": "1200",
+                            "keyName": "1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY",
                             "categories": [
                                 {
                                     "categoryCode": "dedicated_host_disk"
@@ -525,6 +580,7 @@ def _get_package(self):
                         },
                         {
                             "capacity": "242",
+                            "keyName": "242_GB_RAM",
                             "categories": [
                                 {
                                     "categoryCode": "dedicated_host_ram"