Manually added OpenStack API code
diff --git a/src/emuvim/api/openstack/resources/__init__.py b/src/emuvim/api/openstack/resources/__init__.py
new file mode 100644
index 0000000..ffddf54
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/__init__.py
@@ -0,0 +1,10 @@
+from instance_flavor import InstanceFlavor
+from model import Model
+from net import Net
+from port import Port
+from resource import Resource
+from router import Router
+from server import Server
+from stack import Stack
+from template import Template
+from image import Image
\ No newline at end of file
diff --git a/src/emuvim/api/openstack/resources/image.py b/src/emuvim/api/openstack/resources/image.py
new file mode 100644
index 0000000..64942d8
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/image.py
@@ -0,0 +1,17 @@
+import uuid
+from datetime import datetime
+
+
+class Image:
+ def __init__(self, name, id=None):
+ self.name = name
+ if id is None:
+ self.id = str(uuid.uuid4())
+ else:
+ self.id = id
+ self.created = str(datetime.now())
+
+ def __eq__(self, other):
+ if self.name == other.name:
+ return True
+ return False
diff --git a/src/emuvim/api/openstack/resources/instance_flavor.py b/src/emuvim/api/openstack/resources/instance_flavor.py
new file mode 100644
index 0000000..4027ae5
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/instance_flavor.py
@@ -0,0 +1,12 @@
+import uuid
+
+
+class InstanceFlavor:
+ def __init__(self, name, cpu=None, memory=None, memory_unit=None, storage=None, storage_unit=None):
+ self.id = str(uuid.uuid4())
+ self.name = name
+ self.cpu = cpu
+ self.memory = memory
+ self.memory_unit = memory_unit
+ self.storage = storage
+ self.storage_unit = storage_unit
diff --git a/src/emuvim/api/openstack/resources/load_balancer.py b/src/emuvim/api/openstack/resources/load_balancer.py
new file mode 100644
index 0000000..310e31b
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/load_balancer.py
@@ -0,0 +1,6 @@
+class LoadBalancer(object):
+ def __init__(self, name, id=None, flavor=None, image=None, command=None, nw_list=None):
+ self.name = name
+ self.id = id # not set
+ self.out_ports = dict()
+ self.in_ports = dict()
diff --git a/src/emuvim/api/openstack/resources/model.py b/src/emuvim/api/openstack/resources/model.py
new file mode 100644
index 0000000..0bf7bde
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/model.py
@@ -0,0 +1,5 @@
+class Model:
+ def __init__(self, resources=None):
+ if not resources:
+ resources = list()
+ self.resources = resources
diff --git a/src/emuvim/api/openstack/resources/net.py b/src/emuvim/api/openstack/resources/net.py
new file mode 100644
index 0000000..efb22be
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/net.py
@@ -0,0 +1,311 @@
+import re
+
+
+class Net:
+ def __init__(self, name):
+ self.name = name
+ self.id = None
+ self.subnet_name = None
+ self.subnet_id = None
+ self.subnet_creation_time = None
+ self.subnet_update_time = None
+ self.gateway_ip = None
+ self.segmentation_id = None # not set
+ self._cidr = None
+ self.start_end_dict = None
+ self._issued_ip_addresses = dict()
+
+ def get_short_id(self):
+ """
+ Returns a shortened UUID, with only the first 6 characters.
+
+ :return: First 6 characters of the UUID
+ :rtype: ``str``
+ """
+ return str(self.id)[:6]
+
+ def get_new_ip_address(self, port_name):
+ """
+ Calculates the next unused IP Address which belongs to the subnet.
+
+ :param port_name: Specifies the port.
+ :type port_name: ``str``
+ :return: Returns a unused IP Address or none if all are in use.
+ :rtype: ``str``
+ """
+ if self.start_end_dict is None:
+ return None
+
+ int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 2 # First address as network address not usable
+ # Second one is for gateways only
+ int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1 # Last address for broadcasts
+ while int_start_ip in self._issued_ip_addresses and int_start_ip <= int_end_ip:
+ int_start_ip += 1
+
+ if int_start_ip > int_end_ip:
+ return None
+
+ self._issued_ip_addresses[int_start_ip] = port_name
+ return Net.int_2_ip(int_start_ip) + '/' + self._cidr.rsplit('/', 1)[1]
+
+ def assign_ip_address(self, cidr, port_name):
+ """
+ Assigns the IP address to the port if it is currently NOT used.
+
+ :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
+ :type cidr: ``str``
+ :param port_name: The port name
+ :type port_name: ``str``
+ :return: * *False*: If the IP address is already issued or if it is not within this subnet mask.
+ * *True*: Else
+ """
+ int_ip = Net.cidr_2_int(cidr)
+ if int_ip in self._issued_ip_addresses:
+ return False
+
+ int_start_ip = Net.ip_2_int(self.start_end_dict['start']) + 1 # First address as network address not usable
+ int_end_ip = Net.ip_2_int(self.start_end_dict['end']) - 1 # Last address for broadcasts
+ if int_ip < int_start_ip or int_ip > int_end_ip:
+ return False
+
+ self._issued_ip_addresses[int_ip] = port_name
+ return True
+
+ def is_my_ip(self, cidr, port_name):
+ """
+ Checks if the IP is registered for this port name.
+
+ :param cidr: The cidr used by the port - e.g. 10.0.0.1/24
+ :type cidr: ``str``
+ :param port_name: The port name
+ :type port_name: ``str``
+ :return: Returns true if the IP address belongs to the port name. Else it returns false.
+ """
+ int_ip = Net.cidr_2_int(cidr)
+
+ if not int_ip in self._issued_ip_addresses:
+ return False
+
+ if self._issued_ip_addresses[int_ip] == port_name:
+ return True
+ return False
+
+ def withdraw_ip_address(self, ip_address):
+ """
+ Removes the IP address from the list of issued addresses, thus other ports can use it.
+
+ :param ip_address: The issued IP address.
+ :type ip_address: ``str``
+ """
+ if ip_address is None:
+ return
+
+ if "/" in ip_address:
+ address, suffix = ip_address.rsplit('/', 1)
+ else:
+ address = ip_address
+ int_ip_address = Net.ip_2_int(address)
+ if int_ip_address in self._issued_ip_addresses.keys():
+ del self._issued_ip_addresses[int_ip_address]
+
+ def reset_issued_ip_addresses(self):
+ """
+ Resets all issued IP addresses.
+ """
+ self._issued_ip_addresses = dict()
+
+ def update_port_name_for_ip_address(self, ip_address, port_name):
+ """
+ Updates the port name of the issued IP address.
+
+ :param ip_address: The already issued IP address.
+ :type ip_address: ``str``
+ :param port_name: The new port name
+ :type port_name: ``str``
+ """
+ address, suffix = ip_address.rsplit('/', 1)
+ int_ip_address = Net.ip_2_int(address)
+ self._issued_ip_addresses[int_ip_address] = port_name
+
+ def set_cidr(self, cidr):
+ """
+ Sets the CIDR for the subnet. It previously checks for the correct CIDR format.
+
+ :param cidr: The new CIDR for the subnet.
+ :type cidr: ``str``
+ :return: * *True*: When the new CIDR was set successfully.
+ * *False*: If the CIDR format was wrong.
+ :rtype: ``bool``
+ """
+ if cidr is None:
+ if self._cidr is not None:
+ import emuvim.api.heat.ip_handler as IP
+ IP.free_cidr(self._cidr, self.subnet_id)
+ self._cidr = None
+ self.reset_issued_ip_addresses()
+ self.start_end_dict = dict()
+ return True
+ if not Net.check_cidr_format(cidr):
+ return False
+
+ self.reset_issued_ip_addresses()
+ self.start_end_dict = Net.calculate_start_and_end_dict(cidr)
+ self._cidr = cidr
+ return True
+
+ def get_cidr(self):
+ """
+ Gets the CIDR.
+
+ :return: The CIDR
+ :rtype: ``str``
+ """
+ return self._cidr
+
+ def clear_cidr(self):
+ self._cidr = None
+ self.start_end_dict = dict()
+ self.reset_issued_ip_addresses()
+
+ def delete_subnet(self):
+ self.subnet_id = None
+ self.subnet_name = None
+ self.subnet_creation_time = None
+ self.subnet_update_time = None
+ self.set_cidr(None)
+
+ @staticmethod
+ def calculate_start_and_end_dict(cidr):
+ """
+ Calculates the start and end IP address for the subnet.
+
+ :param cidr: The CIDR for the subnet.
+ :type cidr: ``str``
+ :return: Dict with start and end ip address
+ :rtype: ``dict``
+ """
+ address, suffix = cidr.rsplit('/', 1)
+ int_suffix = int(suffix)
+ int_address = Net.ip_2_int(address)
+ address_space = 2 ** 32 - 1
+
+ for x in range(0, 31 - int_suffix):
+ address_space = ~(~address_space | (1 << x))
+
+ start = int_address & address_space
+ end = start + (2 ** (32 - int_suffix) - 1)
+
+ return {'start': Net.int_2_ip(start), 'end': Net.int_2_ip(end)}
+
+ @staticmethod
+ def cidr_2_int(cidr):
+ if cidr is None:
+ return None
+ ip = cidr.rsplit('/', 1)[0]
+ return Net.ip_2_int(ip)
+
+ @staticmethod
+ def ip_2_int(ip):
+ """
+ Converts a IP address to int.
+
+ :param ip: IP address
+ :type ip: ``str``
+ :return: IP address as int.
+ :rtype: ``int``
+ """
+ o = map(int, ip.split('.'))
+ res = (16777216 * o[0]) + (65536 * o[1]) + (256 * o[2]) + o[3]
+ return res
+
+ @staticmethod
+ def int_2_ip(int_ip):
+ """
+ Converts a int IP address to string.
+
+ :param int_ip: Int IP address.
+ :type int_ip: ``int``
+ :return: IP address
+ :rtype: ``str``
+ """
+ o1 = int(int_ip / 16777216) % 256
+ o2 = int(int_ip / 65536) % 256
+ o3 = int(int_ip / 256) % 256
+ o4 = int(int_ip) % 256
+ return '%(o1)s.%(o2)s.%(o3)s.%(o4)s' % locals()
+
+ @staticmethod
+ def check_cidr_format(cidr):
+ """
+ Checks the CIDR format. An valid example is: 192.168.0.0/29
+
+ :param cidr: CIDR to be checked.
+ :type cidr: ``str``
+ :return: * *True*: If the Format is correct.
+ * *False*: If it is not correct.
+ :rtype: ``bool``
+ """
+ r = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{2}')
+ if r.match(cidr):
+ return True
+ return False
+
+ def create_network_dict(self):
+ """
+ Creates the network description dictionary.
+
+ :return: Network description.
+ :rtype: ``dict``
+ """
+ network_dict = dict()
+ network_dict["status"] = "ACTIVE" # TODO do we support inactive networks?
+ if self.subnet_id == None:
+ network_dict["subnets"] = []
+ else:
+ network_dict["subnets"] = [self.subnet_id]
+ network_dict["name"] = self.name
+ network_dict["admin_state_up"] = True # TODO is it always true?
+ network_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456" # TODO what should go in here
+ network_dict["id"] = self.id
+ network_dict["shared"] = False # TODO is it always false?
+ return network_dict
+
+ def create_subnet_dict(self):
+ """
+ Creates the subnet description dictionary.
+
+ :return: Subnet description.
+ :rtype: ``dict``
+ """
+ subnet_dict = dict()
+ subnet_dict["name"] = self.subnet_name
+ subnet_dict["network_id"] = self.id
+ subnet_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456" # TODO what should go in here?
+ subnet_dict["created_at"] = self.subnet_creation_time
+ subnet_dict["dns_nameservers"] = []
+ subnet_dict["allocation_pools"] = [self.start_end_dict]
+ subnet_dict["host_routers"] = []
+ subnet_dict["gateway_ip"] = self.gateway_ip
+ subnet_dict["ip_version"] = "4"
+ subnet_dict["cidr"] = self.get_cidr()
+ subnet_dict["updated_at"] = self.subnet_update_time
+ subnet_dict["id"] = self.subnet_id
+ subnet_dict["enable_dhcp"] = False # TODO do we support DHCP?
+ return subnet_dict
+
+ def __eq__(self, other):
+ if self.name == other.name and self.subnet_name == other.subnet_name and \
+ self.gateway_ip == other.gateway_ip and \
+ self.segmentation_id == other.segmentation_id and \
+ self._cidr == other._cidr and \
+ self.start_end_dict == other.start_end_dict:
+ return True
+ return False
+
+ def __hash__(self):
+ return hash((self.name,
+ self.subnet_name,
+ self.gateway_ip,
+ self.segmentation_id,
+ self._cidr,
+ self.start_end_dict))
diff --git a/src/emuvim/api/openstack/resources/port.py b/src/emuvim/api/openstack/resources/port.py
new file mode 100644
index 0000000..a280fb6
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/port.py
@@ -0,0 +1,170 @@
+import logging
+import threading
+import uuid
+
+lock = threading.Lock()
+intf_names = dict()
+
+
+class Port:
+ def __init__(self, name, ip_address=None, mac_address=None, floating_ip=None):
+ self.name = name
+ self.intf_name = None
+ self.id = str(uuid.uuid4())
+ self.template_name = name
+ """
+ ip_address is structured like 10.0.0.1/24
+ """
+ self.ip_address = ip_address
+ self.mac_address = mac_address
+ self.floating_ip = floating_ip
+ self.net_name = None
+
+ def set_name(self, name):
+ """
+ Sets the port name.
+
+ :param name: New port name.
+ :type name: ``str``
+ """
+ if self.name == name:
+ return
+
+ # Delete old interface name
+ global lock
+ lock.acquire()
+ if intf_names[self.intf_name][0] == self.id and intf_names[self.intf_name][1] is False:
+ del intf_names[self.intf_name]
+ lock.release()
+
+ self.name = name
+ # Create new interface name
+ self.create_intf_name()
+
+ def create_intf_name(self):
+ """
+ Creates the interface name, while using the first 4 letters of the port name, the specification, if it is an
+ 'in' / 'out' port or something else, and a counter value if the name is already used. The counter starts
+ for each name at 0 and can go up to 999. After creating the name each port will post its interface name
+ into the global dictionary and adding his full name. Thus each port can determine if his desired interface
+ name is already used and choose the next one.
+ """
+ split_name = self.name.split(':')
+ if len(split_name) >= 3:
+ if split_name[2] == 'input' or split_name[2] == 'in':
+ self.intf_name = split_name[0][:4] + '-' + \
+ 'in'
+ elif split_name[2] == 'output' or split_name[2] == 'out':
+ self.intf_name = split_name[0][:4] + '-' + \
+ 'out'
+ else:
+ self.intf_name = split_name[0][:4] + '-' + \
+ split_name[2][:4]
+ else:
+ self.intf_name = self.name[:9]
+
+ global lock
+ lock.acquire()
+ counter = 0
+ global intf_names
+ intf_len = len(self.intf_name)
+ self.intf_name = self.intf_name + '-' + str(counter)[:4]
+ while self.intf_name in intf_names and counter < 999 and not intf_names[self.intf_name][0] == self.id:
+ counter += 1
+ self.intf_name = self.intf_name[:intf_len] + '-' + str(counter)[:4]
+
+ if counter >= 1000:
+ logging.ERROR("Port %s could not create unique interface name (%s)", self.name, self.intf_name)
+ lock.release()
+ return
+
+ updated = False
+ if self.intf_name in intf_names and intf_names[self.intf_name][0] == self.id:
+ updated = True
+
+ intf_names[self.intf_name] = [self.id, updated]
+ lock.release()
+
+ def get_short_id(self):
+ """
+ Gets a shortened ID which only contains first 6 characters.
+
+ :return: The first 6 characters of the UUID.
+ :rtype: ``str``
+ """
+ return str(self.id)[:6]
+
+ def create_port_dict(self, compute):
+ """
+ Creates the port description dictionary.
+
+ :param compute: Requires the compute resource to determine the used network.
+ :type compute: :class:`heat.compute`
+ :return: Returns the description dictionary.
+ :rtype: ``dict``
+ """
+ port_dict = dict()
+ port_dict["admin_state_up"] = True # TODO is it always true?
+ port_dict["device_id"] = "257614cc-e178-4c92-9c61-3b28d40eca44" # TODO find real values
+ port_dict["device_owner"] = "" # TODO do we have such things?
+ net = compute.find_network_by_name_or_id(self.net_name)
+ port_dict["fixed_ips"] = [
+ {
+ "ip_address": self.ip_address.rsplit('/', 1)[0] if self.ip_address is not None else "",
+ "subnet_id": net.subnet_id if net is not None else ""
+ }
+ ]
+ port_dict["id"] = self.id
+ port_dict["mac_address"] = self.mac_address
+ port_dict["name"] = self.name
+ port_dict["network_id"] = net.id if net is not None else ""
+ port_dict["status"] = "ACTIVE" # TODO do we support inactive port?
+ port_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456" # TODO find real tenant_id
+ return port_dict
+
+ def compare_attributes(self, other):
+ """
+ Does NOT compare ip_address because this function only exists to check if we can
+ update the IP address without any changes
+
+ :param other: The port to compare with
+ :type other: :class:`heat.resources.port`
+ :return: True if the attributes are the same, else False.
+ :rtype: ``bool``
+ """
+ if other is None:
+ return False
+
+ if self.name == other.name and self.floating_ip == other.floating_ip and \
+ self.net_name == other.net_name:
+ return True
+ return False
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+
+ if self.name == other.name and self.ip_address == other.ip_address and \
+ self.mac_address == other.mac_address and \
+ self.floating_ip == other.floating_ip and \
+ self.net_name == other.net_name:
+ return True
+ return False
+
+ def __hash__(self):
+ return hash((self.name,
+ self.ip_address,
+ self.mac_address,
+ self.floating_ip,
+ self.net_name))
+
+ def __del__(self):
+ global lock
+ lock.acquire()
+ global intf_names
+ if self.intf_name in intf_names and intf_names[self.intf_name][0] == self.id:
+ if intf_names[self.intf_name][1] is False:
+ del intf_names[self.intf_name]
+ else:
+ intf_names[self.intf_name][1] = False
+ lock.release()
diff --git a/src/emuvim/api/openstack/resources/resource.py b/src/emuvim/api/openstack/resources/resource.py
new file mode 100644
index 0000000..5e45e11
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/resource.py
@@ -0,0 +1,5 @@
+class Resource:
+ def __init__(self, name, type=None, properties=None):
+ self.name = name
+ self.type = type
+ self.properties = properties
diff --git a/src/emuvim/api/openstack/resources/router.py b/src/emuvim/api/openstack/resources/router.py
new file mode 100644
index 0000000..2301e26
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/router.py
@@ -0,0 +1,17 @@
+import uuid
+
+
+class Router:
+ def __init__(self, name, id=None):
+ self.name = name
+ self.id = id if id is not None else str(uuid.uuid4())
+ self.subnet_names = list()
+
+ def add_subnet(self, subnet_name):
+ self.subnet_names.append(subnet_name)
+
+ def __eq__(self, other):
+ if self.name == other.name and len(self.subnet_names) == len(other.subnet_names) and \
+ set(self.subnet_names) == set(other.subnet_names):
+ return True
+ return False
diff --git a/src/emuvim/api/openstack/resources/server.py b/src/emuvim/api/openstack/resources/server.py
new file mode 100644
index 0000000..d20a2d4
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/server.py
@@ -0,0 +1,66 @@
+class Server(object):
+ def __init__(self, name, id=None, flavor=None, image=None, command=None, nw_list=None):
+ self.name = name
+ self.full_name = None
+ self.template_name = None
+ self.id = id
+ self.image = image
+ self.command = command
+ self.port_names = list()
+ self.flavor = flavor
+ self.son_emu_command = None
+ self.emulator_compute = None
+
+ def compare_attributes(self, other):
+ """
+ Compares only class attributes like name and flavor but not the list of ports with the other server.
+
+ :param other: The second server to compare with.
+ :type other: :class:`heat.resources.server`
+ :return: * *True*: If all attributes are alike.
+ * *False*: Else
+ :rtype: ``bool``
+ """
+ if self.name == other.name and self.full_name == other.full_name and \
+ self.flavor == other.flavor and \
+ self.image == other.image and \
+ self.command == other.command:
+ return True
+ return False
+
+ def __eq__(self, other):
+ if self.name == other.name and self.full_name == other.full_name and \
+ self.flavor == other.flavor and \
+ self.image == other.image and \
+ self.command == other.command and \
+ len(self.port_names) == len(other.port_names) and \
+ set(self.port_names) == set(other.port_names):
+ return True
+ return False
+
+ def create_server_dict(self, compute=None):
+ """
+ Creates the server description dictionary.
+
+ :param compute: The compute resource for further status information.
+ :type compute: :class:`heat.compute`
+ :return: Server description dictionary.
+ :rtype: ``dict``
+ """
+ server_dict = dict()
+ server_dict['name'] = self.name
+ server_dict['full_name'] = self.full_name
+ server_dict['id'] = self.id
+ server_dict['template_name'] = self.template_name
+ server_dict['flavor'] = self.flavor
+ server_dict['image'] = self.image
+ if self.son_emu_command is not None:
+ server_dict['command'] = self.son_emu_command
+ else:
+ server_dict['command'] = self.command
+
+ if compute is not None:
+ server_dict['status'] = 'ACTIVE'
+ server_dict['OS-EXT-STS:power_state'] = 1
+ server_dict["OS-EXT-STS:task_state"] = None
+ return server_dict
diff --git a/src/emuvim/api/openstack/resources/stack.py b/src/emuvim/api/openstack/resources/stack.py
new file mode 100644
index 0000000..a02cbab
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/stack.py
@@ -0,0 +1,53 @@
+import uuid
+
+
+class Stack:
+ def __init__(self, id=None):
+ self.servers = dict()
+ self.nets = dict()
+ self.ports = dict()
+ self.routers = dict()
+ self.stack_name = None
+ self.creation_time = None
+ self.update_time = None
+ self.status = None
+ if id is None:
+ self.id = str(uuid.uuid4())
+ else:
+ self.id = id
+
+ def add_server(self, server):
+ """
+ Adds one server to the server dictionary.
+
+ :param server: The server to add.
+ :type server: :class:`heat.resources.server`
+ """
+ self.servers[server.name] = server
+
+ def add_net(self, net):
+ """
+ Adds one network to the network dictionary.
+
+ :param net: Network to add.
+ :type net: :class:`heat.resources.net`
+ """
+ self.nets[net.name] = net
+
+ def add_port(self, port):
+ """
+ Adds one port to the port dictionary.
+
+ :param port: Port to add.
+ :type port: :class:`heat.resources.port`
+ """
+ self.ports[port.name] = port
+
+ def add_router(self, router):
+ """
+ Adds one router to the port dictionary.
+
+ :param router: Router to add.
+ :type router: :class:`heat.resources.router`
+ """
+ self.routers[router.name] = router
diff --git a/src/emuvim/api/openstack/resources/template.py b/src/emuvim/api/openstack/resources/template.py
new file mode 100644
index 0000000..2e55f64
--- /dev/null
+++ b/src/emuvim/api/openstack/resources/template.py
@@ -0,0 +1,4 @@
+class Template:
+ def __init__(self, resources=None):
+ self.version = '2015-04-30'
+ self.resources = resources