blob: 532337bafe5c160cdb3b7a176202bf3470c3ef03 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import unittest
from libcloud.test import MockHttp
from libcloud.utils.py3 import httplib
from libcloud.common.vultr import VultrException
from libcloud.test.file_fixtures import ComputeFileFixtures
from libcloud.compute.drivers.vultr import VultrNodeDriver, VultrNodeDriverV2
class VultrTestsV2(unittest.TestCase):
def setUp(self):
VultrNodeDriver.connectionCls.conn_class = VultrMockHttpV2
VultrMockHttpV2.type = None
self.driver = VultrNodeDriver("foo")
def test_unknown_api_version(self):
self.assertRaises(NotImplementedError, VultrNodeDriver, "foo", api_version="3")
def test_correct_class_is_used(self):
self.assertIsInstance(self.driver, VultrNodeDriverV2)
def test_list_sizes(self):
sizes = self.driver.list_sizes()
self.assertEqual(len(sizes), 25)
size = sizes[0]
self.assertEqual(size.id, "vc2-1c-1gb")
self.assertEqual(size.name, "vc2-1c-1gb")
for size in sizes:
self.assertIsInstance(size.price, int)
self.assertIsInstance(size.ram, int)
self.assertIsInstance(size.disk, int)
def test_list_images(self):
images = self.driver.list_images()
self.assertEqual(len(images), 30)
image = images[0]
self.assertEqual(image.id, "124")
self.assertEqual(image.name, "Windows 2012 R2 x64")
self.assertEqual(image.extra["arch"], "x64")
self.assertEqual(image.extra["family"], "windows")
def test_list_locations(self):
locations = self.driver.list_locations()
self.assertEqual(len(locations), 17)
location = locations[0]
self.assertEqual(location.country, "NL")
self.assertEqual(location.id, "ams")
self.assertEqual(location.name, "Amsterdam")
self.assertIsInstance(location.extra["option"], list)
def test_list_nodes(self):
nodes = self.driver.list_nodes(ex_list_bare_metals=False)
self.assertEqual(len(nodes), 3)
node = nodes[0]
self.assertEqual(node.id, "123")
self.assertEqual(node.name, "test1")
self.assertEqual(node.image, "477")
self.assertEqual(node.size, "vc2-1c-2gb")
self.assertEqual(node.extra["location"], "fra")
self.assertIn("45.76.83.44", node.public_ips)
for node in nodes:
self.assertIsInstance(node.public_ips, list)
self.assertIsInstance(node.private_ips, list)
self.assertIsInstance(node.extra["vcpu_count"], int)
self.assertIsInstance(node.extra["ram"], int)
self.assertIsInstance(node.extra["disk"], int)
self.assertIsInstance(node.extra["allowed_bandwidth"], int)
def test_list_nodes_with_bare_metals(self):
nodes = self.driver.list_nodes()
self.assertEqual(len(nodes), 5)
node = nodes[0]
self.assertEqual(node.id, "123")
self.assertEqual(node.name, "test1")
self.assertEqual(node.image, "477")
self.assertEqual(node.size, "vc2-1c-2gb")
self.assertEqual(node.extra["vcpu_count"], 1)
self.assertEqual(node.extra["location"], "fra")
self.assertFalse(node.extra["is_bare_metal"])
self.assertIn("45.76.83.44", node.public_ips)
node = nodes[-1]
self.assertEqual(node.id, "234")
self.assertEqual(node.size, "vbm-8c-132gb")
self.assertEqual(node.state, "pending")
self.assertEqual(node.extra["cpu_count"], 8)
self.assertEqual(node.extra["location"], "mia")
self.assertTrue(node.extra["is_bare_metal"])
self.assertEqual(node.extra["mac_address"], 189250955239968)
def test_create_node(self):
name = "test123"
image = self.driver.list_images()[0]
size = self.driver.list_sizes()[0]
location = self.driver.list_locations()[0]
node = self.driver.create_node(name=name, image=image, size=size, location=location)
self.assertEqual(node.id, "123")
self.assertEqual(node.name, "test123")
self.assertEqual(node.image, "446")
self.assertFalse(node.extra["is_bare_metal"])
def test_destroy_node(self):
node = self.driver.list_nodes()[0]
response = self.driver.destroy_node(node)
self.assertTrue(response)
def test_start_node(self):
node = self.driver.list_nodes()[0]
response = self.driver.start_node(node)
self.assertTrue(response)
def test_reboot_node(self):
node = self.driver.list_nodes()[0]
response = self.driver.reboot_node(node)
self.assertTrue(response)
def test_ex_stop_nodes(self):
nodes = self.driver.list_nodes()
response = self.driver.ex_stop_nodes(nodes)
self.assertTrue(response)
def test_stop_node(self):
node = self.driver.list_nodes()[0]
response = self.driver.stop_node(node)
self.assertTrue(response)
def test_ex_get_node(self):
node = self.driver.ex_get_node("123")
self.assertEqual(node.name, "test")
self.assertEqual(node.id, "123")
self.assertEqual(node.size, "vc2-1c-1gb")
self.assertEqual(node.image, "477")
self.assertEqual(node.state, "running")
self.assertIn("45.76.36.72", node.public_ips)
def test_ex_resize_node(self):
node = self.driver.ex_get_node("123")
size = self.driver.list_sizes()[1]
node = self.driver.ex_resize_node(node, size)
self.assertEqual(node.id, "123")
self.assertEqual(node.size, "vc2-1c-2gb")
self.assertEqual(node.name, "test4")
self.assertIn("192.248.168.21", node.public_ips)
def test_list_key_pairs(self):
keys = self.driver.list_key_pairs()
self.assertEqual(len(keys), 1)
key = keys[0]
self.assertEqual(key.name, "tester")
self.assertIsNone(key.private_key)
self.assertEqual(key.extra["id"], "123")
def test_list_key_pairs_UNAUTHORIZED(self):
VultrMockHttpV2.type = "UNAUTHORIZED"
with self.assertRaises(VultrException):
self.driver.list_key_pairs()
def test_get_key_pair(self):
key_id = "123"
key = self.driver.get_key_pair(key_id)
self.assertEqual(key.name, "tester")
self.assertEqual(key.extra["id"], "123")
self.assertIsNone(key.private_key)
def test_import_key_pair_from_string(self):
name = "tester"
key_material = "material"
key = self.driver.import_key_pair_from_string(name, key_material)
self.assertEqual(key.name, "tester")
self.assertEqual(key.extra["id"], "123")
self.assertIsNone(key.private_key)
def test_delete_key_pair(self):
keys = self.driver.list_key_pairs()
key = keys[0]
response = self.driver.delete_key_pair(key)
self.assertTrue(response)
def test_list_volumes(self):
volumes = self.driver.list_volumes()
self.assertEqual(len(volumes), 2)
volume = volumes[0]
self.assertEqual(volume.name, "test1")
self.assertEqual(volume.id, "123")
self.assertEqual(volume.size, 10)
self.assertEqual(volume.state, "available")
def test_create_volume(self):
volume = self.driver.create_volume(size=15, name="test4", location="ewr")
self.assertEqual(volume.name, "test4")
self.assertEqual(volume.id, "ec6d1ecc-aa70-4f18-8edd-887a258b1b45")
self.assertEqual(volume.size, 15)
self.assertEqual(volume.state, "creating")
self.assertEqual(volume.extra["location"], "ewr")
def test_attach_volume(self):
volume = self.driver.list_volumes()[0]
node = self.driver.list_nodes()[0]
response = self.driver.attach_volume(node, volume)
self.assertTrue(response)
def test_attach_volume_WRONG_LOCATION(self):
volume = self.driver.list_volumes()[1]
node = self.driver.list_nodes()[0]
VultrMockHttpV2.type = "WRONG_LOCATION"
with self.assertRaises(VultrException):
self.driver.attach_volume(node, volume)
def test_detach_volume(self):
volume = self.driver.list_volumes()[0]
response = self.driver.detach_volume(volume)
self.assertTrue(response)
def test_destroy_volume(self):
volume = self.driver.list_volumes()[0]
response = self.driver.destroy_volume(volume)
self.assertTrue(response)
def test_ex_get_volume(self):
volume = self.driver.ex_get_volume("123")
self.assertEqual(volume.id, "123")
self.assertEqual(volume.name, "test2")
self.assertEqual(volume.state, "available")
def test_ex_resize_volume(self):
volume = self.driver.list_volumes()[0]
response = self.driver.ex_resize_volume(volume, 20)
self.assertTrue(response)
def test_ex_list_available_sizes_for_location(self):
location = self.driver.list_locations()[0]
available_sizes = self.driver.ex_list_available_sizes_for_location(location)
self.assertTrue(isinstance(available_sizes, list))
def test_ex_list_networks(self):
networks = self.driver.ex_list_networks()
self.assertEqual(len(networks), 2)
network = networks[0]
self.assertEqual(network.id, "123")
self.assertEqual(network.cidr_block, "10.7.96.0/20")
self.assertEqual(network.location, "ams")
def test_ex_get_network(self):
network = self.driver.ex_get_network("123")
self.assertEqual(network.id, "123")
self.assertEqual(network.cidr_block, "10.7.96.0/20")
self.assertEqual(network.location, "ams")
def test_ex_create_network(self):
network = self.driver.ex_create_network("10.0.0.0/24", "ams", "TestNetwork")
self.assertEqual(network.id, "123")
self.assertEqual(network.cidr_block, "10.0.0.0/24")
self.assertEqual(network.location, "ams")
def test_ex_destroy_network(self):
network = self.driver.ex_get_network("123")
response = self.driver.ex_destroy_network(network)
self.assertTrue(response)
def ex_list_snapshots(self):
snapshots = self.driver.ex_list_snapshots()
self.assertEqual(len(snapshots), 2)
snapshot = snapshots[0]
self.assertEqual(snapshot.id, "123")
self.assertEqual(snapshot.size, "25.0")
self.assertEqual(snapshot.state, "available")
snapshot = snapshots[1]
self.assertEqual(snapshot.id, "1234")
self.assertEqual(snapshot.size, "25.0")
self.assertEqual(snapshot.state, "creating")
def test_ex_get_snapshot(self):
snapshot = self.driver.ex_get_snapshot("123")
self.assertEqual(snapshot.id, "123")
self.assertEqual(snapshot.size, 25.0)
self.assertEqual(snapshot.state, "available")
def test_ex_create_snapshot(self):
node = self.driver.list_nodes()[0]
snapshot = self.driver.ex_create_snapshot(node)
self.assertEqual(snapshot.id, "123")
self.assertEqual(snapshot.size, 0)
self.assertEqual(snapshot.state, "creating")
def test_ex_delete_snapshot(self):
snapshot = self.driver.ex_get_snapshot("123")
response = self.driver.ex_delete_snapshot(snapshot)
self.assertTrue(response)
def test_ex_list_bare_metal_sizes(self):
sizes = self.driver.ex_list_bare_metal_sizes()
self.assertEqual(len(sizes), 4)
for size in sizes:
self.assertIsInstance(size.extra["cpu_count"], int)
self.assertIsInstance(size.extra["cpu_threads"], int)
self.assertIsInstance(size.extra["cpu_model"], str)
def test_create_bare_metal_node(self):
image = self.driver.list_images()[0]
location = self.driver.list_locations()[0]
size = self.driver.list_sizes()[-1]
node = self.driver.create_node(name="test1", image=image, location=location, size=size)
self.assertEqual(node.name, "test1")
self.assertEqual(node.id, "234")
self.assertTrue(node.extra["is_bare_metal"])
self.assertEqual(node.extra["cpu_count"], 4)
def test_reboot_bare_metal_node(self):
nodes = self.driver.list_nodes()
node = nodes[-1]
self.assertTrue(node.extra["is_bare_metal"])
response = self.driver.reboot_node(node)
self.assertTrue(response)
def test_start_bare_metal_node(self):
nodes = self.driver.list_nodes()
node = nodes[-1]
self.assertTrue(node.extra["is_bare_metal"])
response = self.driver.start_node(node)
self.assertTrue(response)
def test_stop_bare_metal_node(self):
nodes = self.driver.list_nodes()
node = nodes[-1]
self.assertTrue(node.extra["is_bare_metal"])
response = self.driver.stop_node(node)
self.assertTrue(response)
def test_destroy_bare_metal_node(self):
nodes = self.driver.list_nodes()
node = nodes[-1]
self.assertTrue(node.extra["is_bare_metal"])
response = self.driver.destroy_node(node)
self.assertTrue(response)
def test_pagination(self):
images = self.driver.list_images()
VultrMockHttpV2.type = "PAGINATED"
paginated_images = self.driver.list_images()
self.assertEqual(len(images), len(paginated_images))
for first, second in zip(images, paginated_images):
self.assertEqual(first.id, second.id)
self.assertEqual(first.name, second.name)
self.assertDictEqual(first.extra, second.extra)
class VultrMockHttpV2(MockHttp):
fixtures = ComputeFileFixtures("vultr_v2")
def _v2_regions(self, method, url, body, headers):
body = self.fixtures.load("list_locations.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_os(self, method, url, body, headers):
body = self.fixtures.load("list_images.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_plans(self, method, url, body, headers):
body = self.fixtures.load("list_sizes.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_instances(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("list_nodes.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("create_node.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_bare_metals(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_list_bare_metal_nodes.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("create_node_bare_metal.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_instances_123(self, method, url, body, headers):
if method == "DELETE":
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
elif method == "GET":
body = self.fixtures.load("ex_get_node.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "PATCH":
body = self.fixtures.load("ex_resize_node.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_instances_123_start(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_instances_123_reboot(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_instances_halt(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_ssh_keys(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("list_key_pairs.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("import_key_pair_from_string.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_ssh_keys_UNAUTHORIZED(self, method, url, body, headers):
body = '{"error":"Invalid API token.","status":401}'
return (httplib.UNAUTHORIZED, body, {}, httplib.responses[httplib.UNAUTHORIZED])
def _v2_ssh_keys_123(self, method, url, body, headers):
if method == "DELETE":
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "GET":
body = self.fixtures.load("get_key_pair.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_blocks(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("list_volumes.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("create_volume.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_blocks_123(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_get_volume.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "DELETE":
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
elif method == "PATCH":
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_blocks_123_attach(self, method, url, body, headers):
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_blocks_1234_attach_WRONG_LOCATION(self, method, url, body, headers):
body = (
'{"error": "unable to attach: Block storage volume must be in '
'the same region as the server it is attached to.",'
' "status": 400}'
)
return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.BAD_REQUEST])
def _v2_blocks_123_detach(self, method, url, body, headers):
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_regions_ams_availability(self, method, url, body, headers):
body = self.fixtures.load("ex_list_available_sizes_for_location.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_private_networks(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_list_networks.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("ex_create_network.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_private_networks_123(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_get_network.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "DELETE":
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_os_PAGINATED(self, method, url, body, headers):
if "cursor" not in url:
body = self.fixtures.load("list_images_paginated_1.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif "cursor=bmV4dF9fMjMw" in url:
body = self.fixtures.load("list_images_paginated_2.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
else:
body = self.fixtures.load("list_images_paginated_3.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_snapshots(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_list_snapshots.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "POST":
body = self.fixtures.load("ex_create_snapshot.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _v2_snapshots_123(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load("ex_get_snapshot.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
elif method == "DELETE":
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_bare_metals_234_reboot(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_bare_metals_234_start(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_bare_metals_234_halt(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_bare_metals_234(self, method, url, body, headers):
return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
def _v2_plans_metal(self, method, url, body, headers):
body = self.fixtures.load("ex_list_bare_metal_sizes.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
if __name__ == "__main__":
sys.exit(unittest.main())