Return actually reachable IP as part of the Floating IP API
[osm/vim-emu.git] / src / emuvim / api / openstack / resources / port.py
1 # Copyright (c) 2015 SONATA-NFV and Paderborn University
2 # ALL RIGHTS RESERVED.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # 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 #
16 # Neither the name of the SONATA-NFV, Paderborn University
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior written
19 # permission.
20 #
21 # This work has been performed in the framework of the SONATA project,
22 # funded by the European Commission under Grant number 671517 through
23 # the Horizon 2020 and 5G-PPP programmes. The authors would like to
24 # acknowledge the contributions of their colleagues of the SONATA
25 # partner consortium (www.sonata-nfv.eu).
26 import logging
27 import threading
28 import uuid
29
30 lock = threading.Lock()
31 intf_names = dict()
32
33
34 class Port:
35 def __init__(self, name, ip_address=None,
36 mac_address=None, floating_ip=None):
37 self.name = name
38 self.intf_name = None
39 self.id = str(uuid.uuid4())
40 self.template_name = name
41 """
42 ip_address is structured like 10.0.0.1/24
43 """
44 self.ip_address = ip_address
45 self.mac_address = mac_address
46 self.floating_ip = floating_ip
47 self.net_name = None
48 self.assigned_container = None
49
50 def set_name(self, name):
51 """
52 Sets the port name.
53
54 :param name: New port name.
55 :type name: ``str``
56 """
57 if self.name == name:
58 return
59
60 # Delete old interface name
61 global lock
62 lock.acquire()
63 if intf_names[self.intf_name][0] == self.id and intf_names[self.intf_name][1] is False:
64 del intf_names[self.intf_name]
65 lock.release()
66
67 self.name = name
68 # Create new interface name
69 self.create_intf_name()
70
71 def create_intf_name(self):
72 """
73 Creates the interface name, while using the first 4 letters of the port name, the specification, if it is an
74 'in' / 'out' port or something else, and a counter value if the name is already used. The counter starts
75 for each name at 0 and can go up to 999. After creating the name each port will post its interface name
76 into the global dictionary and adding his full name. Thus each port can determine if his desired interface
77 name is already used and choose the next one.
78 """
79 split_name = self.name.split(':')
80 if len(split_name) >= 3:
81 if split_name[2] == 'input' or split_name[2] == 'in':
82 self.intf_name = split_name[0][:4] + '-' + \
83 'in'
84 elif split_name[2] == 'output' or split_name[2] == 'out':
85 self.intf_name = split_name[0][:4] + '-' + \
86 'out'
87 else:
88 self.intf_name = split_name[0][:4] + '-' + \
89 split_name[2][:4]
90 else:
91 self.intf_name = self.name[:9]
92
93 global lock
94 lock.acquire()
95 counter = 0
96 global intf_names
97 intf_len = len(self.intf_name)
98 self.intf_name = self.intf_name + '-' + str(counter)[:4]
99 while self.intf_name in intf_names and counter < 999 and not intf_names[
100 self.intf_name][0] == self.id:
101 counter += 1
102 self.intf_name = self.intf_name[:intf_len] + '-' + str(counter)[:4]
103
104 if counter >= 1000:
105 logging.ERROR(
106 "Port %s could not create unique interface name (%s)", self.name, self.intf_name)
107 lock.release()
108 return
109
110 updated = False
111 if self.intf_name in intf_names and intf_names[self.intf_name][0] == self.id:
112 updated = True
113
114 intf_names[self.intf_name] = [self.id, updated]
115 lock.release()
116
117 def get_short_id(self):
118 """
119 Gets a shortened ID which only contains first 6 characters.
120
121 :return: The first 6 characters of the UUID.
122 :rtype: ``str``
123 """
124 return str(self.id)[:6]
125
126 def create_port_dict(self, compute):
127 """
128 Creates the port description dictionary.
129
130 :param compute: Requires the compute resource to determine the used network.
131 :type compute: :class:`heat.compute`
132 :return: Returns the description dictionary.
133 :rtype: ``dict``
134 """
135 port_dict = dict()
136 port_dict["admin_state_up"] = True # TODO is it always true?
137 # TODO find real values
138 port_dict["device_id"] = "257614cc-e178-4c92-9c61-3b28d40eca44"
139 port_dict["device_owner"] = "" # TODO do we have such things?
140 net = compute.find_network_by_name_or_id(self.net_name)
141 port_dict["fixed_ips"] = [
142 {
143 "ip_address": self.ip_address.rsplit('/', 1)[0] if self.ip_address is not None else "",
144 "subnet_id": net.subnet_id if net is not None else ""
145 }
146 ]
147 port_dict["id"] = self.id
148 port_dict["mac_address"] = self.mac_address
149 port_dict["name"] = self.name
150 port_dict["network_id"] = net.id if net is not None else ""
151 port_dict["status"] = "ACTIVE" # TODO do we support inactive port?
152 # TODO find real tenant_id
153 port_dict["tenant_id"] = "abcdefghijklmnopqrstuvwxyz123456"
154 return port_dict
155
156 def compare_attributes(self, other):
157 """
158 Does NOT compare ip_address because this function only exists to check if we can
159 update the IP address without any changes
160
161 :param other: The port to compare with
162 :type other: :class:`heat.resources.port`
163 :return: True if the attributes are the same, else False.
164 :rtype: ``bool``
165 """
166 if other is None:
167 return False
168
169 if self.name == other.name and self.floating_ip == other.floating_ip and \
170 self.net_name == other.net_name:
171 return True
172 return False
173
174 def __eq__(self, other):
175 if other is None:
176 return False
177
178 if self.name == other.name and self.ip_address == other.ip_address and \
179 self.mac_address == other.mac_address and \
180 self.floating_ip == other.floating_ip and \
181 self.net_name == other.net_name:
182 return True
183 return False
184
185 def __hash__(self):
186 return hash((self.name,
187 self.ip_address,
188 self.mac_address,
189 self.floating_ip,
190 self.net_name))
191
192 def __del__(self):
193 global lock
194 lock.acquire()
195 global intf_names
196 if self.intf_name in intf_names and intf_names[self.intf_name][0] == self.id:
197 if intf_names[self.intf_name][1] is False:
198 del intf_names[self.intf_name]
199 else:
200 intf_names[self.intf_name][1] = False
201 lock.release()