diff --git a/implementation/vm/instance.py b/implementation/vm/instance.py index d5f82ed..dea0165 100644 --- a/implementation/vm/instance.py +++ b/implementation/vm/instance.py @@ -1,5 +1,5 @@ from interface.vm.instance import InstanceInterface -from interface.vm.resources import Instance +from interface.vm.resources import Instance, Flavor from openstack.exceptions import SDKException from novaclient import client import logging @@ -24,8 +24,8 @@ def openstackError(func): try: return func(*args, **kw) except SDKException as e: - logging.error(e.get_error_message()) - new_e = Exception(e.get_error_message()) + logging.error(e) + new_e = Exception(e) new_e.OpenStackError = True raise new_e return wrap_OpenStackError @@ -38,24 +38,27 @@ class OSVirtualMachineManager(InstanceInterface): self.openstack = cloud @openstackError - def create_base_vm(self, name, resource, networks, block_dev_map): - flavor = self.get_flavor(resource) - new_server = self.compute.create_server(name=name, + def create_base_vm(self, name, flavor, networks, block_dev_map): + devices = [] + b_device = block_dev_map.__dict__ + devices.append(b_device) + flavor = self.get_flavor(flavor) + new_server = self.openstack.compute.create_server(name=name, flavorRef=flavor.id, networks=networks, - block_device_mapping=block_dev_map + block_device_mapping=devices ) - return new_server + return self.convert_server_to_instance(new_server) @openstackError - def create_vm_from_template(self, name, image, resource, networks): - self.create_multiple_vm_from_template(name, image, resource, networks, 1) + def create_vm_from_template(self, name, image, flavor, networks): + self.create_multiple_vm_from_template(name, image, flavor, networks, 1) @openstackError - def create_multiple_vm_from_template(self, name, image, resource, networks, + def create_multiple_vm_from_template(self, name, image, flavor, networks, number, **args): compute = self.openstack.compute - flav = compute.find_flavor(resource) + flav = compute.find_flavor(flavor) image = compute.find_image(image) if not image: @@ -67,76 +70,104 @@ class OSVirtualMachineManager(InstanceInterface): networks=networks, min_count=number, ) + return self.convert_server_to_instance(new_server) - new_server = self.openstack.compute.wait_for_server(new_server) + @openstackError + def create_flavor(self, name, ram, vcpus, initial_disk): + flavor = self.openstack.compute.create_flavor(name=name, + ram=ram, + vcpus=vcpus, + disk=initial_disk) + return Flavor(flavor.name, flavor.id, flavor.ram, + flavor.vcpus, flavor.disk) - return new_server + @openstackError + def get_flavor(self, flavor_id): + flavor = self.openstack.compute.find_flavor(flavor_id) + return Flavor(flavor.name, flavor.id, flavor.ram, + flavor.vcpus, flavor.disk) + + @openstackError + def delete_flavor(self, flavor_id): + flavor = self.openstack.compute.find_flavor(flavor_id) + self.openstack.compute.delete_flavor(flavor) + + @openstackError + def list_flavors(self): + flavors = [] + for flavor in self.openstack.compute.flavors(): + flavors.append(Flavor(flavor.name, flavor.id, flavor.ram, + flavor.vcpus, flavor.disk)) + return flavors @openstackError def get_vm(self, name_or_id=None): if not name_or_id: raise ValueError("Name or id doesn't given") - server_instance = self.openstack.get_server(name_or_id) + server_instance = self.openstack.compute.get_server(name_or_id) if not server_instance: raise ValueError("Could not get the vm") - return server_instance + return self.convert_server_to_instance(server_instance) @openstackError def start_vm(self, name_or_id=None): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.start_server(instance) @openstackError def stop_vm(self, name_or_id=None): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.stop_server(instance) @openstackError def suspend_vm(self, name_or_id=None): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.suspend_server(instance) @openstackError def wake_up_vm(self, name_or_id=None): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.resume_server(instance) @openstackError def reboot_vm(self, name_or_id): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.reboot_server(instance, reboot_type='SOFT') @openstackError def reset_vm(self, name_or_id): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.reboot_server(instance, reboot_type='HARD') @openstackError def destroy_vm(self, name_or_id): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) self.openstack.compute.delete_server(instance) @openstackError def get_status(self, name_or_id): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.compute.get_server(name_or_id) return instance.status @openstackError def list_all_vm(self): - return self.openstack.compute.servers() + servers = [] + for server in self.openstack.compute.servers(): + servers.append(self.convert_server_to_instance(server)) + return servers @openstackError def resize_vm(self, name_or_id, resource): if name_or_id: - instance = self.get_vm(name_or_id) + instance = self.openstack.get_server(name_or_id) flavor = self.openstack.compute.find_flavor(resource['name']) self.openstack.compute.resize_server(instance, flavor) @@ -144,17 +175,30 @@ class OSVirtualMachineManager(InstanceInterface): @openstackError def create_template(self, name_or_id, template_name, metadata=None): if name_or_id: - instance = self.get_vm(name_or_id) - self.openstack.compute.create_server_image(instance, template_name, metadata) + instance = self.openstack.compute.get_server(name_or_id) + self.openstack.compute.create_server_image(instance, + template_name, + metadata) - def get_vnc_console(self, name_or_id): + def get_vnc_console(self, server_id): with client.Client("2", session=sess) as nova: - if name_or_id: - instance = nova.servers.get(name_or_id) + if server_id: + instance = nova.servers.get(server_id) return instance.get_vnc_console("novnc") - def attach_volume(self, name_or_id, amount): - raise NotImplementedError + @openstackError + def attach_volume(self, server_id, volume_id, device=None): + self.openstack.compute.create_volume_attachment(server_id, + {"volumeId": volume_id, + "device": device + }) + @openstackError + def detach_volume(self, server_id, volume_id, device=None): + self.openstack.compute.delete_volume_attachment(server_id, + {"volumeId": volume_id, + "device": device + }) + def convert_server_to_instance(self, server): if not server.image: @@ -162,7 +206,7 @@ class OSVirtualMachineManager(InstanceInterface): else: image_id = server.image.id return Instance(id=server.id, - resource=server.flavor.id, + flavor=server.flavor.id, name=server.name, image_id=image_id, disks=server.volumes, diff --git a/interface/vm/instance.py b/interface/vm/instance.py index 6e6aa27..2b948fc 100644 --- a/interface/vm/instance.py +++ b/interface/vm/instance.py @@ -7,10 +7,13 @@ It should be implemented for using other providers e. g. OpenStack class InstanceInterface: - def create_vm_from_template(self, template, resource): + def create_base_vm(self, name, flavor, networks, block_device_mapping): raise NotImplementedError - def create_multiple_vm_from_template(self, template, resource, number): + def create_vm_from_template(self, name, image, flavor, networks): + raise NotImplementedError + + def create_multiple_vm_from_template(self, image, flavor, networks, number): raise NotImplementedError def get_vm(self, name_or_id): @@ -40,9 +43,6 @@ class InstanceInterface: def destroy_vm(self, name_or_id): raise NotImplementedError - def migrate_vm(self, name_or_id, to): - raise NotImplementedError - def get_status(self, name_or_id): raise NotImplementedError @@ -52,17 +52,14 @@ class InstanceInterface: def install_ssh_key(self, name_or_id, key): raise NotImplementedError - def save_as_template(self, name_or_id): + def get_vnc_console(self, name_or_id): raise NotImplementedError - def get_vnc_console(self, name_or_id): + def change_password(self, name_or_id): raise NotImplementedError - # def change_password(self, name_or_id): - # raise NotImplementedError - # - # def get_password(self, name_or_id): - # raise NotImplementedError + def get_password(self, name_or_id): + raise NotImplementedError def resize_vm(self, name_or_id): raise NotImplementedError diff --git a/interface/vm/resources.py b/interface/vm/resources.py index b6fd44a..eb58e10 100644 --- a/interface/vm/resources.py +++ b/interface/vm/resources.py @@ -10,11 +10,11 @@ class Instance: ssh_keys = None console_access_url = None - def __init__(self, id, name, resource, image_id, - status, addresses, launched_at, terminated_at, disks=None): + def __init__(self, id, name, flavor, image_id, status, + addresses, launched_at, terminated_at, disks=None): self.id = id self.name = name - self.resource = resource + self.flavor = flavor self.image = image_id self.disks = disks self.status = status @@ -34,6 +34,20 @@ class Flavor: self.vcpus = vcpus self.initial_disk = disk + def JSON(self): + return json.dumps(self.__dict__) + -class Volume: - pass +class BlockDeviceMapping: + def __init__(self, boot_index, uuid, source_type, volume_size, + destination_type, delete_on_termination, disk_bus): + self.boot_index = boot_index + self.uuid=uuid + self.source_type=source_type + self.volume_size=volume_size + self.destination_type=destination_type + self.delete_on_termination=delete_on_termination + self.disk_bus=disk_bus + + def JSON(self): + return json.dumps(self.__dict__) diff --git a/main.py b/main.py index c0124fb..358f3f0 100644 --- a/main.py +++ b/main.py @@ -1,25 +1,44 @@ # This file is for testing the openstack api access from implementation.vm.instance import OSVirtualMachineManager +from interface.vm.resources import BlockDeviceMapping, Flavor import openstack # openstack.enable_logging(debug=True) conn = openstack.connect(cloud='openstack') +block_dev_map = BlockDeviceMapping( + boot_index=0, + uuid="da51253f-867c-472d-8ce0-81e7b7126d60", + source_type="image", + volume_size=10, + destination_type="volume", + delete_on_termination=True, + disk_bus="scsi" +) +networks = [{"uuid": "c03d0d4b-413e-4cc6-9ebe-c0b5ca0dac3a"}] + interface = OSVirtualMachineManager(conn) -resource = {"name": "m1.tiny", "ram": 1024, "cpu": 2} print('#'*40) -print(resource) # interface.create_vm_from_template('new_server', resource, "cirros-0.4.0-x86_64-disk") # interface.stop_vm('a6bc504f-a422-4492-b429-e5dad2df12f4') -server = interface.get_vm("New test") +# server = interface.get_vm("8e94c162-f8ed-4872-b9e5-50bf33040b5b") + +# print(interface.delete_flavor("flavorTest")) + +for flavor in interface.list_flavors(): + print(flavor.JSON()) + +# print(interface.create_base_vm("BaseTest", "2", networks, block_dev_map)) + +# print(interface.convert_server_to_instance(server).JSON()) -print(interface.convert_server_to_instance(server).JSON()) +# print(interface.openstack.compute.get_server("8e94c162-f8ed-4872-b9e5-50bf33040b5b")) -print(interface.get_status("Uborka Test")) +# print(server.JSON()) -print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b")) +# print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b")) # print(interface.convert_server_to_instance(server)) @@ -37,4 +56,3 @@ print(interface.get_vnc_console("8e94c162-f8ed-4872-b9e5-50bf33040b5b")) # interface.wake_up_vm(server.id) # interface.destroy_vm(server.id) -# conn.compute.change_server_password(server, "root") diff --git a/tests/vmtest.py b/tests/vmtest.py new file mode 100644 index 0000000..1bb2e77 --- /dev/null +++ b/tests/vmtest.py @@ -0,0 +1,55 @@ +import unittest +from unittest.mock import MagicMock +from implementation.vm.instance import OSVirtualMachineManager +from interface.vm.resources import Instance + +servers = [ + MagicMock( + id="test1", + name="test1", + flavorRef="flav1", + imageRef="image1", + networks= [{"uuid": "network1"}], + terminated_at="20200320-15-31", + launched_at="20190510-13-22", + disks=["disk1"], + status="ACTIVE", + addresses=[{"mac": "12345678AB", "ipv4": "1.1.1.1"}] + ) +] + + +class MockOpenStackCompute(MagicMock): + def setUp(self): + self.compute = MagicMock() + self.compute.create_server = MagicMock(return_value=servers[0]) + self.compute.find_flavor = MagicMock(return_value=MagicMock(id="uuid1")) + self.compute.find_image = MagicMock(return_value=MagicMock(id="uuid2")) + self.compute.wait_for_server = MagicMock(return_value=True) + self.compute.list_servers = MagicMock(return_value=servers) + + +class InstanceCreateTestCase(unittest.TestCase): + def setUp(self): + self.conn = MockOpenStackCompute() + self.conn.setUp() + + self.manager = OSVirtualMachineManager(self.conn) + + def tearDown(self): + pass + + def test_create_from_template(self): + instance = self.manager.create_vm_from_template('test', 'imageid', 'flavorid', + ['networkid1']) + self.conn.compute.create_server.assert_called() + + def test_create_from_template_params(self): + self.manager.create_vm_from_template('test', 'imageid', 'flavorid', + ['networkid1']) + self.conn.compute.create_server.assert_called_with( + name='test', + flavorRef="uuid1", + imageRef="uuid2", + networks=['networkid1'], + min_count=1)