--- /dev/null
+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()