1# Licensed to the Apache Software Foundation (ASF) under one or more
2# contributor license agreements.  See the NOTICE file distributed with
3# this work for additional information regarding copyright ownership.
4# The ASF licenses this file to You under the Apache License, Version 2.0
5# (the "License"); you may not use this file except in compliance with
6# the License.  You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import unittest
17import sys
18import pytest
19
20from libcloud.common.types import InvalidCredsError
21
22from libcloud.utils.py3 import httplib
23from libcloud.utils.py3 import xmlrpclib
24from libcloud.utils.py3 import next
25
26from libcloud.compute.drivers.softlayer import SoftLayerNodeDriver as SoftLayer
27from libcloud.compute.drivers.softlayer import SoftLayerException, \
28    NODE_STATE_MAP
29from libcloud.compute.types import NodeState, KeyPairDoesNotExistError
30
31from libcloud.test import unittest
32from libcloud.test import MockHttp               # pylint: disable-msg=E0611
33from libcloud.test.file_fixtures import ComputeFileFixtures
34from libcloud.test.secrets import SOFTLAYER_PARAMS
35
36null_fingerprint = '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:' + \
37                   '00:00:00:00:00'
38
39
40class SoftLayerTests(unittest.TestCase):
41
42    def setUp(self):
43        SoftLayer.connectionCls.conn_class = SoftLayerMockHttp
44        SoftLayerMockHttp.type = None
45        self.driver = SoftLayer(*SOFTLAYER_PARAMS)
46
47    def test_list_nodes(self):
48        nodes = self.driver.list_nodes()
49        node = nodes[0]
50        self.assertEqual(node.name, 'libcloud-testing1.example.com')
51        self.assertEqual(node.state, NodeState.RUNNING)
52        self.assertEqual(node.extra['password'], 'L3TJVubf')
53
54    def test_initializing_state(self):
55        nodes = self.driver.list_nodes()
56        node = nodes[1]
57        self.assertEqual(node.state, NODE_STATE_MAP['INITIATING'])
58
59    def test_list_locations(self):
60        locations = self.driver.list_locations()
61        dal = next(l for l in locations if l.id == 'dal05')
62        self.assertEqual(dal.country, 'US')
63        self.assertEqual(dal.id, 'dal05')
64        self.assertEqual(dal.name, 'Dallas - Central U.S.')
65
66    def test_list_images(self):
67        images = self.driver.list_images()
68        image = images[0]
69        self.assertEqual(image.id, 'CENTOS_6_64')
70
71    def test_get_image(self):
72        image = self.driver.get_image('CENTOS_6_64')
73        self.assertEqual(image.id, 'CENTOS_6_64')
74
75    def test_fail_get_image(self):
76        self.assertRaises(
77            SoftLayerException,
78            self.driver.get_image,
79            'NOT_IMAGE')
80
81    def test_list_sizes(self):
82        sizes = self.driver.list_sizes()
83        self.assertEqual(len(sizes), 13)
84        for size in sizes:
85            self.assertTrue(size.price > 0.0)
86
87    def test_create_node(self):
88        node = self.driver.create_node(name="libcloud-testing",
89                                       location=self.driver.list_locations()[0],
90                                       size=self.driver.list_sizes()[0],
91                                       image=self.driver.list_images()[0])
92        self.assertEqual(node.name, 'libcloud-testing.example.com')
93        self.assertEqual(node.state, NODE_STATE_MAP['RUNNING'])
94
95    def test_create_node_ex_hourly_True(self):
96        SoftLayerMockHttp.type = 'HOURLY_BILLING_TRUE'
97
98        node = self.driver.create_node(name="libcloud-testing",
99                                       location=self.driver.list_locations()[0],
100                                       size=self.driver.list_sizes()[0],
101                                       image=self.driver.list_images()[0],
102                                       ex_hourly=True)
103        self.assertEqual(node.name, 'libcloud-testing.example.com')
104        self.assertEqual(node.state, NODE_STATE_MAP['RUNNING'])
105
106        SoftLayerMockHttp.type = 'HOURLY_BILLING_FALSE'
107
108        node = self.driver.create_node(name="libcloud-testing",
109                                       location=self.driver.list_locations()[0],
110                                       size=self.driver.list_sizes()[0],
111                                       image=self.driver.list_images()[0],
112                                       ex_hourly=False)
113        self.assertEqual(node.name, 'libcloud-testing.example.com')
114        self.assertEqual(node.state, NODE_STATE_MAP['RUNNING'])
115
116
117    def test_create_fail(self):
118        SoftLayerMockHttp.type = "SOFTLAYEREXCEPTION"
119        self.assertRaises(
120            SoftLayerException,
121            self.driver.create_node,
122            name="SOFTLAYEREXCEPTION",
123            location=self.driver.list_locations()[0],
124            size=self.driver.list_sizes()[0],
125            image=self.driver.list_images()[0])
126
127    def test_create_creds_error(self):
128        SoftLayerMockHttp.type = "INVALIDCREDSERROR"
129        self.assertRaises(
130            InvalidCredsError,
131            self.driver.create_node,
132            name="INVALIDCREDSERROR",
133            location=self.driver.list_locations()[0],
134            size=self.driver.list_sizes()[0],
135            image=self.driver.list_images()[0])
136
137    def test_create_node_no_location(self):
138        self.driver.create_node(name="Test",
139                                size=self.driver.list_sizes()[0],
140                                image=self.driver.list_images()[0])
141
142    def test_create_node_no_image(self):
143        self.driver.create_node(name="Test", size=self.driver.list_sizes()[0])
144
145    def test_create_node_san(self):
146        self.driver.create_node(name="Test", ex_local_disk=False)
147
148    def test_create_node_domain_for_name(self):
149        self.driver.create_node(name="libcloud.org")
150
151    def test_create_node_ex_options(self):
152        self.driver.create_node(name="Test",
153                                location=self.driver.list_locations()[0],
154                                size=self.driver.list_sizes()[0],
155                                image=self.driver.list_images()[0],
156                                ex_domain='libcloud.org',
157                                ex_cpus=2,
158                                ex_ram=2048,
159                                ex_disk=100,
160                                ex_keyname='test1',
161                                ex_bandwidth=10,
162                                ex_local_disk=False,
163                                ex_datacenter='Dal05',
164                                ex_os='UBUNTU_LATEST')
165
166    def test_reboot_node(self):
167        node = self.driver.list_nodes()[0]
168        self.driver.reboot_node(node)
169
170    def test_destroy_node(self):
171        node = self.driver.list_nodes()[0]
172        self.driver.destroy_node(node)
173
174    def test_list_keypairs(self):
175        keypairs = self.driver.list_key_pairs()
176        self.assertEqual(len(keypairs), 2)
177        self.assertEqual(keypairs[0].name, 'test1')
178        self.assertEqual(keypairs[0].fingerprint, null_fingerprint)
179
180    def test_get_key_pair(self):
181        key_pair = self.driver.get_key_pair(name='test1')
182        self.assertEqual(key_pair.name, 'test1')
183
184    def test_get_key_pair_does_not_exist(self):
185        self.assertRaises(KeyPairDoesNotExistError, self.driver.get_key_pair,
186                          name='test-key-pair')
187
188    @pytest.mark.skip(reason="no way of currently testing this")
189    def test_create_key_pair(self):
190        key_pair = self.driver.create_key_pair(name='my-key-pair')
191        fingerprint = ('1f:51:ae:28:bf:89:e9:d8:1f:25:5d'
192                       ':37:2d:7d:b8:ca:9f:f5:f1:6f')
193
194        self.assertEqual(key_pair.name, 'my-key-pair')
195        self.assertEqual(key_pair.fingerprint, fingerprint)
196        self.assertTrue(key_pair.private_key is not None)
197
198    def test_delete_key_pair(self):
199        success = self.driver.delete_key_pair('test1')
200        self.assertTrue(success)
201
202
203class SoftLayerMockHttp(MockHttp, unittest.TestCase):
204    fixtures = ComputeFileFixtures('softlayer')
205
206    def _get_method_name(self, type, use_param, qs, path):
207        return "_xmlrpc"
208
209    def _xmlrpc(self, method, url, body, headers):
210        params, meth_name = xmlrpclib.loads(body)
211        url = url.replace("/", "_")
212        meth_name = "%s_%s" % (url, meth_name)
213        return getattr(self, meth_name)(method, url, body, headers)
214
215    def _xmlrpc_v3_SoftLayer_Virtual_Guest_getCreateObjectOptions(
216            self, method, url, body, headers):
217        body = self.fixtures.load(
218            'v3__SoftLayer_Virtual_Guest_getCreateObjectOptions.xml')
219        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
220
221    def _xmlrpc_v3_SoftLayer_Account_getVirtualGuests(
222            self, method, url, body, headers):
223        body = self.fixtures.load('v3_SoftLayer_Account_getVirtualGuests.xml')
224        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
225
226    def _xmlrpc_v3_SoftLayer_Location_Datacenter_getDatacenters(
227            self, method, url, body, headers):
228        body = self.fixtures.load(
229            'v3_SoftLayer_Location_Datacenter_getDatacenters.xml')
230        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
231
232    def _xmlrpc_v3_SoftLayer_Virtual_Guest_createObject(
233            self, method, url, body, headers):
234        fixture = {
235            None: 'v3__SoftLayer_Virtual_Guest_createObject.xml',
236            'INVALIDCREDSERROR': 'SoftLayer_Account.xml',
237            'SOFTLAYEREXCEPTION': 'fail.xml',
238            'HOURLY_BILLING_TRUE': 'v3__SoftLayer_Virtual_Guest_createObject.xml',
239            'HOURLY_BILLING_FALSE': 'v3__SoftLayer_Virtual_Guest_createObject.xml',
240        }[self.type]
241
242        if self.type == 'HOURLY_BILLING_TRUE':
243            # Verify parameter is sent as a boolean and not as a string
244            expected_value = """
245<member>
246<name>hourlyBillingFlag</name>
247<value><boolean>1</boolean></value>
248</member>
249""".strip()
250
251            self.assertTrue(expected_value in body, 'Request body is missing hourlyBillingFlag attribute')
252        elif self.type == 'HOURLY_BILLING_FALSE':
253            # Verify parameter is sent as a boolean and not as a string
254            expected_value = """
255<member>
256<name>hourlyBillingFlag</name>
257<value><boolean>0</boolean></value>
258</member>
259""".strip()
260
261            self.assertTrue(expected_value in body, 'Request body is missing hourlyBillingFlag attribute')
262
263
264        body = self.fixtures.load(fixture)
265        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
266
267    def _xmlrpc_v3_SoftLayer_Virtual_Guest_getObject(
268            self, method, url, body, headers):
269        body = self.fixtures.load(
270            'v3__SoftLayer_Virtual_Guest_getObject.xml')
271        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
272
273    def _xmlrpc_v3_SoftLayer_Virtual_Guest_rebootSoft(
274            self, method, url, body, headers):
275        body = self.fixtures.load('empty.xml')
276        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
277
278    def _xmlrpc_v3_SoftLayer_Virtual_Guest_deleteObject(
279            self, method, url, body, headers):
280        body = self.fixtures.load('empty.xml')
281        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
282
283    def _xmlrpc_v3_SoftLayer_Account_getSshKeys(
284            self, method, url, body, headers):
285        body = self.fixtures.load('v3__SoftLayer_Account_getSshKeys.xml')
286        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
287
288    def _xmlrpc_v3_SoftLayer_Security_Ssh_Key_getObject(
289            self, method, url, body, headers):
290        body = self.fixtures.load('v3__SoftLayer_Security_Ssh_Key_getObject.xml')
291        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
292
293    def _xmlrpc_v3_SoftLayer_Security_Ssh_Key_createObject(
294            self, method, url, body, headers):
295        body = self.fixtures.load('v3__SoftLayer_Security_Ssh_Key_createObject.xml')
296        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
297
298    def _xmlrpc_v3_SoftLayer_Security_Ssh_Key_deleteObject(
299            self, method, url, body, headers):
300        body = self.fixtures.load('v3__SoftLayer_Security_Ssh_Key_deleteObject.xml')
301        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
302
303
304if __name__ == '__main__':
305    sys.exit(unittest.main())
306