Revision eb647cfe
b/kamaki/cli/argument/__init__.py | ||
---|---|---|
52 | 52 |
This is the top-level Argument class. It is suggested to extent this |
53 | 53 |
class into more specific argument types. |
54 | 54 |
""" |
55 |
lvalue_delimiter = '/' |
|
55 | 56 |
|
56 | 57 |
def __init__(self, arity, help=None, parsed_name=None, default=None): |
57 | 58 |
self.arity = int(arity) |
... | ... | |
86 | 87 |
*self.parsed_name, |
87 | 88 |
dest=name, action=action, default=self.default, help=self.help) |
88 | 89 |
|
90 |
@property |
|
91 |
def lvalue(self): |
|
92 |
"""A printable form of the left value when calling an argument e.g., |
|
93 |
--left-value=right-value""" |
|
94 |
return (self.lvalue_delimiter or ' ').join(self.parsed_name or []) |
|
95 |
|
|
89 | 96 |
|
90 | 97 |
class ConfigArgument(Argument): |
91 | 98 |
"""Manage a kamaki configuration (file)""" |
b/kamaki/cli/commands/__init__.py | ||
---|---|---|
263 | 263 |
else: |
264 | 264 |
raise CLIInvalidArgument( |
265 | 265 |
'Invalid value %s for argument %s' % ( |
266 |
newvalue, '/'.join(self.parsed_name)),
|
|
266 |
newvalue, self.lvalue),
|
|
267 | 267 |
details=['Valid output formats: %s' % ', '.join(self.formats)]) |
268 | 268 |
|
269 | 269 |
|
b/kamaki/cli/commands/cyclades.py | ||
---|---|---|
415 | 415 |
'Connect server to network w. floating ip ( NETWORK_ID,IP )' |
416 | 416 |
'(can be repeated)', |
417 | 417 |
'--network-with-ip'), |
418 |
automatic_ip=FlagArgument( |
|
419 |
'Automatically assign an IP to the server', '--automatic-ip') |
|
418 | 420 |
) |
419 | 421 |
required = ('server_name', 'flavor_id', 'image_id') |
420 | 422 |
|
421 | 423 |
@errors.cyclades.cluster_size |
422 | 424 |
def _create_cluster(self, prefix, flavor_id, image_id, size): |
423 |
networks = [dict(network=netid) for netid in ( |
|
424 |
self['network_id'] or [])] + (self['network_id_and_ip'] or []) |
|
425 |
if self['automatic_ip']: |
|
426 |
networks = [] |
|
427 |
else: |
|
428 |
networks = [dict(network=netid) for netid in ( |
|
429 |
(self['network_id'] or []) + (self['network_id_and_ip'] or []) |
|
430 |
)] or None |
|
425 | 431 |
servers = [dict( |
426 | 432 |
name='%s%s' % (prefix, i if size > 1 else ''), |
427 | 433 |
flavor_id=flavor_id, |
... | ... | |
473 | 479 |
|
474 | 480 |
def main(self): |
475 | 481 |
super(self.__class__, self)._run() |
482 |
if self['automatic_ip'] and ( |
|
483 |
self['network_id'] or self['network_id_and_ip']): |
|
484 |
raise CLIInvalidArgument('Invalid argument combination', details=[ |
|
485 |
'Argument %s should not be combined with other' % ( |
|
486 |
self.arguments['automatic_ip'].lvalue), |
|
487 |
'network-related arguments i.e., %s or %s' % ( |
|
488 |
self.arguments['network_id'].lvalue, |
|
489 |
self.arguments['network_id_and_ip'].lvalue)]) |
|
476 | 490 |
self._run( |
477 | 491 |
name=self['server_name'], |
478 | 492 |
flavor_id=self['flavor_id'], |
b/kamaki/cli/commands/pithos.py | ||
---|---|---|
377 | 377 |
if self['publish'] and self['unpublish']: |
378 | 378 |
raise CLIInvalidArgument( |
379 | 379 |
'Arguments %s and %s cannot be used together' % ( |
380 |
'/'.join(self.arguments['publish'].parsed_name),
|
|
381 |
'/'.join(self.arguments['publish'].parsed_name)))
|
|
380 |
self.arguments['publish'].lvalue,
|
|
381 |
self.arguments['publish'].lvalue))
|
|
382 | 382 |
if self['no_permissions'] and ( |
383 | 383 |
self['uuid_for_read_permission'] or self[ |
384 | 384 |
'uuid_for_write_permission']): |
385 | 385 |
raise CLIInvalidArgument( |
386 |
'%s cannot be used with other permission arguments' % '/'.join(
|
|
387 |
self.arguments['no_permissions'].parsed_name))
|
|
386 |
'%s cannot be used with other permission arguments' % ( |
|
387 |
self.arguments['no_permissions'].lvalue))
|
|
388 | 388 |
self._run() |
389 | 389 |
|
390 | 390 |
|
... | ... | |
555 | 555 |
self.dst_client.account, |
556 | 556 |
self.dst_client.container, |
557 | 557 |
dst_path), |
558 |
'Use %s to transfer overwrite' % ('/'.join(
|
|
559 |
self.arguments['force'].parsed_name))])
|
|
558 |
'Use %s to transfer overwrite' % ( |
|
559 |
self.arguments['force'].lvalue)])
|
|
560 | 560 |
else: |
561 | 561 |
# One object transfer |
562 | 562 |
try: |
... | ... | |
570 | 570 |
'Missing specific path container %s' % self.container, |
571 | 571 |
importance=2, details=[ |
572 | 572 |
'To transfer container contents %s' % ( |
573 |
'/'.join(self.arguments[ |
|
574 |
'source_prefix'].parsed_name))]) |
|
573 |
self.arguments['source_prefix'].lvalue)]) |
|
575 | 574 |
raise |
576 | 575 |
dst_path = self.dst_path or self.path |
577 | 576 |
dst_obj = dst_objects.get(dst_path or self.path, None) |
... | ... | |
589 | 588 |
self.container, |
590 | 589 |
self.path), |
591 | 590 |
'To recursively copy a directory, use', |
592 |
' %s' % ('/'.join( |
|
593 |
self.arguments['source_prefix'].parsed_name)), |
|
591 |
' %s' % self.arguments['source_prefix'].lvalue, |
|
594 | 592 |
'To create a file, use', |
595 | 593 |
' /file create (general purpose)', |
596 | 594 |
' /file mkdir (a directory object)']) |
... | ... | |
607 | 605 |
self.dst_client.account, |
608 | 606 |
self.dst_client.container, |
609 | 607 |
dst_path), |
610 |
'Use %s to transfer overwrite' % ('/'.join(
|
|
611 |
self.arguments['force'].parsed_name))])
|
|
608 |
'Use %s to transfer overwrite' % ( |
|
609 |
self.arguments['force'].lvalue)])
|
|
612 | 610 |
return pairs |
613 | 611 |
|
614 | 612 |
def _run(self, source_path_or_url, destination_path_or_url=''): |
... | ... | |
873 | 871 |
if path.isdir(lpath): |
874 | 872 |
if not self['recursive']: |
875 | 873 |
raise CLIError('%s is a directory' % lpath, details=[ |
876 |
'Use %s to upload directories & contents' % '/'.join(
|
|
877 |
self.arguments['recursive'].parsed_name)])
|
|
874 |
'Use %s to upload directories & contents' % ( |
|
875 |
self.arguments['recursive'].lvalue)])
|
|
878 | 876 |
robj = self.client.container_get(path=rpath) |
879 | 877 |
if not self['overwrite']: |
880 | 878 |
if robj.json: |
... | ... | |
1174 | 1172 |
elif path.exists(lpath): |
1175 | 1173 |
raise CLIError( |
1176 | 1174 |
'Cannot overwrite %s' % lpath, |
1177 |
details=['To overwrite/resume, use %s' % '/'.join(
|
|
1178 |
self.arguments['resume'].parsed_name)])
|
|
1175 |
details=['To overwrite/resume, use %s' % ( |
|
1176 |
self.arguments['resume'].lvalue)])
|
|
1179 | 1177 |
else: |
1180 | 1178 |
ret.append((opath, lpath, None)) |
1181 | 1179 |
elif self.path: |
1182 | 1180 |
raise CLIError( |
1183 | 1181 |
'Remote object /%s/%s is a directory' % ( |
1184 | 1182 |
self.container, local_path), |
1185 |
details=['Use %s to download directories' % '/'.join(
|
|
1186 |
self.arguments['recursive'].parsed_name)])
|
|
1183 |
details=['Use %s to download directories' % ( |
|
1184 |
self.arguments['recursive'].lvalue)])
|
|
1187 | 1185 |
else: |
1188 |
parsed_name = '/'.join(self.arguments['recursive'].parsed_name)
|
|
1186 |
parsed_name = self.arguments['recursive'].lvalue
|
|
1189 | 1187 |
raise CLIError( |
1190 | 1188 |
'Cannot download container %s' % self.container, |
1191 | 1189 |
details=[ |
... | ... | |
1197 | 1195 |
if path.exists(local_path) and not self['resume']: |
1198 | 1196 |
raise CLIError( |
1199 | 1197 |
'Cannot overwrite local file %s' % (lpath), |
1200 |
details=['To overwrite/resume, use %s' % '/'.join(
|
|
1201 |
self.arguments['resume'].parsed_name)])
|
|
1198 |
details=['To overwrite/resume, use %s' % ( |
|
1199 |
self.arguments['resume'].lvalue)])
|
|
1202 | 1200 |
ret.append((rpath, local_path, self['resume'])) |
1203 | 1201 |
for r, l, resume in ret: |
1204 | 1202 |
if r: |
... | ... | |
1528 | 1526 |
delimiter, msg = '/', 'Empty and d%s' % msg[1:] |
1529 | 1527 |
elif num_of_contents: |
1530 | 1528 |
raise CLIError('Container %s is not empty' % container, details=[ |
1531 |
'Use %s to delete non-empty containers' % '/'.join(
|
|
1532 |
self.arguments['recursive'].parsed_name)])
|
|
1529 |
'Use %s to delete non-empty containers' % ( |
|
1530 |
self.arguments['recursive'].lvalue)])
|
|
1533 | 1531 |
if self['yes'] or self.ask_user(msg): |
1534 | 1532 |
if num_of_contents: |
1535 | 1533 |
self.client.del_container(delimiter=delimiter) |
... | ... | |
1658 | 1656 |
else: |
1659 | 1657 |
raise CLISyntaxError( |
1660 | 1658 |
'No valid users specified, use %s or %s' % ( |
1661 |
'/'.join(self.arguments['user_uuid'].parsed_name),
|
|
1662 |
'/'.join(self.arguments['username'].parsed_name)),
|
|
1659 |
self.arguments['user_uuid'].lvalue,
|
|
1660 |
self.arguments['username'].lvalue),
|
|
1663 | 1661 |
details=[ |
1664 | 1662 |
'Check if a username or uuid is valid with', |
1665 | 1663 |
' user uuid2username', 'OR', ' user username2uuid']) |
b/kamaki/clients/compute/__init__.py | ||
---|---|---|
128 | 128 |
:param personality: a list of (file path, file contents) tuples, |
129 | 129 |
describing files to be injected into virtual server upon creation |
130 | 130 |
|
131 |
:param networks: (list of dicts) Networks to connect to, list this: |
|
132 |
"networks": [ |
|
133 |
{"network": <network_uuid>}, |
|
134 |
{"network": <network_uuid>, "fixed_ip": address}, |
|
135 |
{"port": <port_id>}, ...] |
|
136 |
ATTENTION: Empty list is different to None. None means ' do not |
|
137 |
mention it', empty list means 'automatically get an ip' |
|
138 |
|
|
131 | 139 |
:returns: a dict with the new virtual server details |
132 | 140 |
|
133 | 141 |
:raises ClientError: wraps request errors |
... | ... | |
141 | 149 |
if personality: |
142 | 150 |
req['server']['personality'] = personality |
143 | 151 |
|
144 |
if networks: |
|
145 |
req['server']['networks'] = networks |
|
152 |
if networks is not None:
|
|
153 |
req['server']['networks'] = networks or []
|
|
146 | 154 |
|
147 | 155 |
r = self.servers_post( |
148 | 156 |
json_data=req, |
b/kamaki/clients/cyclades/__init__.py | ||
---|---|---|
61 | 61 |
{"network": <network_uuid>}, |
62 | 62 |
{"network": <network_uuid>, "fixed_ip": address}, |
63 | 63 |
{"port": <port_id>}, ...] |
64 |
ATTENTION: Empty list is different to None. None means ' do not |
|
65 |
mention it', empty list means 'automatically get an ip' |
|
64 | 66 |
|
65 | 67 |
:returns: a dict with the new virtual server details |
66 | 68 |
|
... | ... | |
76 | 78 |
|
77 | 79 |
return super(CycladesClient, self).create_server( |
78 | 80 |
name, flavor_id, image_id, |
79 |
metadata=metadata, personality=personality) |
|
81 |
metadata=metadata, personality=personality, networks=networks)
|
|
80 | 82 |
|
81 | 83 |
def start_server(self, server_id): |
82 | 84 |
"""Submit a startup request |
... | ... | |
157 | 159 |
r = self.servers_stats_get(server_id) |
158 | 160 |
return r.json['stats'] |
159 | 161 |
|
160 |
def list_networks(self, detail=False): |
|
161 |
""" |
|
162 |
:param detail: (bool) |
|
163 |
|
|
164 |
:returns: (list) id,name if not detail else full info per network |
|
165 |
""" |
|
166 |
detail = 'detail' if detail else '' |
|
167 |
r = self.networks_get(command=detail) |
|
168 |
return r.json['networks'] |
|
169 |
|
|
170 |
def list_network_nics(self, network_id): |
|
171 |
""" |
|
172 |
:param network_id: integer (str or int) |
|
173 |
|
|
174 |
:returns: (list) |
|
175 |
""" |
|
176 |
r = self.networks_get(network_id=network_id) |
|
177 |
return r.json['network']['attachments'] |
|
178 |
|
|
179 |
def create_network( |
|
180 |
self, name, |
|
181 |
cidr=None, gateway=None, type=None, dhcp=False): |
|
182 |
""" |
|
183 |
:param name: (str) |
|
184 |
|
|
185 |
:param cidr: (str) |
|
186 |
|
|
187 |
:param geteway: (str) |
|
188 |
|
|
189 |
:param type: (str) if None, will use MAC_FILTERED as default |
|
190 |
Valid values: CUSTOM, IP_LESS_ROUTED, MAC_FILTERED, PHYSICAL_VLAN |
|
191 |
|
|
192 |
:param dhcp: (bool) |
|
193 |
|
|
194 |
:returns: (dict) network detailed info |
|
195 |
""" |
|
196 |
net = dict(name=name) |
|
197 |
if cidr: |
|
198 |
net['cidr'] = cidr |
|
199 |
if gateway: |
|
200 |
net['gateway'] = gateway |
|
201 |
net['type'] = type or 'MAC_FILTERED' |
|
202 |
net['dhcp'] = True if dhcp else False |
|
203 |
req = dict(network=net) |
|
204 |
r = self.networks_post(json_data=req, success=202) |
|
205 |
return r.json['network'] |
|
206 |
|
|
207 |
def get_network_details(self, network_id): |
|
208 |
""" |
|
209 |
:param network_id: integer (str or int) |
|
210 |
|
|
211 |
:returns: (dict) |
|
212 |
""" |
|
213 |
r = self.networks_get(network_id=network_id) |
|
214 |
return r.json['network'] |
|
215 |
|
|
216 |
def update_network_name(self, network_id, new_name): |
|
217 |
""" |
|
218 |
:param network_id: integer (str or int) |
|
219 |
|
|
220 |
:param new_name: (str) |
|
221 |
|
|
222 |
:returns: (dict) response headers |
|
223 |
""" |
|
224 |
req = {'network': {'name': new_name}} |
|
225 |
r = self.networks_put(network_id=network_id, json_data=req) |
|
226 |
return r.headers |
|
227 |
|
|
228 |
def delete_network(self, network_id): |
|
229 |
""" |
|
230 |
:param network_id: integer (str or int) |
|
231 |
|
|
232 |
:returns: (dict) response headers |
|
233 |
|
|
234 |
:raises ClientError: 421 Network in use |
|
235 |
""" |
|
236 |
try: |
|
237 |
r = self.networks_delete(network_id) |
|
238 |
return r.headers |
|
239 |
except ClientError as err: |
|
240 |
if err.status == 421: |
|
241 |
err.details = [ |
|
242 |
'Network may be still connected to at least one server'] |
|
243 |
raise |
|
244 |
|
|
245 |
def connect_server(self, server_id, network_id): |
|
246 |
""" Connect a server to a network |
|
247 |
|
|
248 |
:param server_id: integer (str or int) |
|
249 |
|
|
250 |
:param network_id: integer (str or int) |
|
251 |
|
|
252 |
:returns: (dict) response headers |
|
253 |
""" |
|
254 |
req = {'add': {'serverRef': server_id}} |
|
255 |
r = self.networks_post(network_id, 'action', json_data=req) |
|
256 |
return r.headers |
|
257 |
|
|
258 |
def disconnect_server(self, server_id, nic_id): |
|
259 |
""" |
|
260 |
:param server_id: integer (str or int) |
|
261 |
|
|
262 |
:param nic_id: (str) |
|
263 |
|
|
264 |
:returns: (int) the number of nics disconnected |
|
265 |
""" |
|
266 |
vm_nets = self.list_server_nics(server_id) |
|
267 |
num_of_disconnections = 0 |
|
268 |
for (nic_id, network_id) in [( |
|
269 |
net['id'], |
|
270 |
net['network_id']) for net in vm_nets if nic_id == net['id']]: |
|
271 |
req = {'remove': {'attachment': '%s' % nic_id}} |
|
272 |
self.networks_post(network_id, 'action', json_data=req) |
|
273 |
num_of_disconnections += 1 |
|
274 |
return num_of_disconnections |
|
275 |
|
|
276 |
def disconnect_network_nics(self, netid): |
|
277 |
""" |
|
278 |
:param netid: integer (str or int) |
|
279 |
""" |
|
280 |
for nic in self.list_network_nics(netid): |
|
281 |
req = dict(remove=dict(attachment=nic)) |
|
282 |
self.networks_post(netid, 'action', json_data=req) |
|
283 |
|
|
284 | 162 |
def wait_server( |
285 | 163 |
self, server_id, |
286 | 164 |
current_status='BUILD', |
... | ... | |
308 | 186 |
return self._wait( |
309 | 187 |
server_id, current_status, get_status, delay, max_wait, wait_cb) |
310 | 188 |
|
311 |
def wait_network( |
|
312 |
self, net_id, |
|
313 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
314 |
"""Wait for network while its status is current_status |
|
315 |
|
|
316 |
:param net_id: integer (str or int) |
|
317 |
|
|
318 |
:param current_status: (str) PENDING | ACTIVE | DELETED |
|
319 |
|
|
320 |
:param delay: time interval between retries |
|
321 |
|
|
322 |
:max_wait: (int) timeout in secconds |
|
323 |
|
|
324 |
:param wait_cb: if set a progressbar is used to show progress |
|
325 |
|
|
326 |
:returns: (str) the new mode if succesfull, (bool) False if timed out |
|
327 |
""" |
|
328 |
|
|
329 |
def get_status(self, net_id): |
|
330 |
r = self.get_network_details(net_id) |
|
331 |
return r['status'], None |
|
332 |
|
|
333 |
return self._wait( |
|
334 |
net_id, current_status, get_status, delay, max_wait, wait_cb) |
|
335 |
|
|
336 | 189 |
def wait_firewall( |
337 | 190 |
self, server_id, |
338 | 191 |
current_status='DISABLED', delay=1, max_wait=100, wait_cb=None): |
... | ... | |
358 | 211 |
server_id, current_status, get_status, delay, max_wait, wait_cb) |
359 | 212 |
|
360 | 213 |
|
361 |
class CycladesNetworkClient(NetworkClient, Waiter):
|
|
214 |
class CycladesNetworkClient(NetworkClient): |
|
362 | 215 |
"""Cyclades Network API extentions""" |
363 | 216 |
|
364 | 217 |
network_types = ( |
... | ... | |
379 | 232 |
return r.json['network'] |
380 | 233 |
|
381 | 234 |
def create_port( |
382 |
self, network_id, device_id, |
|
383 |
security_groups=None, name=None, fixed_ips=None): |
|
384 |
port = dict(network_id=network_id, device_id=device_id) |
|
235 |
self, network_id, |
|
236 |
device_id=None, security_groups=None, name=None, fixed_ips=None): |
|
237 |
port = dict(network_id=network_id) |
|
238 |
if device_id: |
|
239 |
port['device_id'] = device_id |
|
385 | 240 |
if security_groups: |
386 | 241 |
port['security_groups'] = security_groups |
387 | 242 |
if name: |
388 | 243 |
port['name'] = name |
389 |
for fixed_ip in fixed_ips: |
|
244 |
for fixed_ip in fixed_ips or []:
|
|
390 | 245 |
diff = set(['subnet_id', 'ip_address']).difference(fixed_ip) |
391 | 246 |
if diff: |
392 | 247 |
raise ValueError( |
... | ... | |
396 | 251 |
r = self.ports_post(json_data=dict(port=port), success=201) |
397 | 252 |
return r.json['port'] |
398 | 253 |
|
399 |
def wait_network(
|
|
400 |
self, net_id,
|
|
401 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None):
|
|
254 |
def create_floatingip(self, floating_network_id, floating_ip_address=''):
|
|
255 |
return super(CycladesNetworkClient, self).create_floatingip(
|
|
256 |
floating_network_id, floating_ip_address=floating_ip_address)
|
|
402 | 257 |
|
403 |
def get_status(self, net_id): |
|
404 |
r = self.get_network_details(net_id) |
|
405 |
return r['status'], None |
|
406 |
|
|
407 |
return self._wait( |
|
408 |
net_id, current_status, get_status, delay, max_wait, wait_cb) |
|
409 |
|
|
410 |
def wait_port( |
|
411 |
self, port_id, |
|
412 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
413 |
|
|
414 |
def get_status(self, net_id): |
|
415 |
r = self.get_port_details(port_id) |
|
416 |
return r['status'], None |
|
417 |
|
|
418 |
return self._wait( |
|
419 |
port_id, current_status, get_status, delay, max_wait, wait_cb) |
|
258 |
def update_floatingip(self, floating_network_id, floating_ip_address=''): |
|
259 |
"""To nullify something optional, use None""" |
|
260 |
return super(CycladesNetworkClient, self).update_floatingip( |
|
261 |
floating_network_id, floating_ip_address=floating_ip_address) |
b/kamaki/clients/network/__init__.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from kamaki.clients import ClientError |
|
34 |
from kamaki.clients import ClientError, Waiter
|
|
35 | 35 |
from kamaki.clients.network.rest_api import NetworkRestClient |
36 | 36 |
|
37 | 37 |
|
38 |
class NetworkClient(NetworkRestClient): |
|
38 |
class NetworkClient(NetworkRestClient, Waiter):
|
|
39 | 39 |
"""OpenStack Network API 2.0 client""" |
40 | 40 |
|
41 | 41 |
def list_networks(self): |
... | ... | |
362 | 362 |
def delete_floatingip(self, floatingip_id): |
363 | 363 |
r = self.floatingips_delete(floatingip_id, success=204) |
364 | 364 |
return r.headers |
365 |
|
|
366 |
# Wait methods |
|
367 |
|
|
368 |
def wait_network( |
|
369 |
self, net_id, |
|
370 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
371 |
|
|
372 |
def get_status(self, net_id): |
|
373 |
r = self.get_network_details(net_id) |
|
374 |
return r['status'], None |
|
375 |
|
|
376 |
return self._wait( |
|
377 |
net_id, current_status, get_status, delay, max_wait, wait_cb) |
|
378 |
|
|
379 |
def wait_subnet( |
|
380 |
self, subnet_id, |
|
381 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
382 |
|
|
383 |
def get_status(self, subnet_id): |
|
384 |
r = self.get_subnet_details(subnet_id) |
|
385 |
return r['status'], None |
|
386 |
|
|
387 |
return self._wait( |
|
388 |
subnet_id, current_status, get_status, delay, max_wait, wait_cb) |
|
389 |
|
|
390 |
def wait_port( |
|
391 |
self, port_id, |
|
392 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
393 |
|
|
394 |
def get_status(self, net_id): |
|
395 |
r = self.get_port_details(port_id) |
|
396 |
return r['status'], None |
|
397 |
|
|
398 |
return self._wait( |
|
399 |
port_id, current_status, get_status, delay, max_wait, wait_cb) |
|
400 |
|
|
401 |
def wait_floatingip( |
|
402 |
self, floatingip_id, |
|
403 |
current_status='PENDING', delay=1, max_wait=100, wait_cb=None): |
|
404 |
|
|
405 |
def get_status(self, floatingip_id): |
|
406 |
r = self.get_network_details(floatingip_id) |
|
407 |
return r['status'], None |
|
408 |
|
|
409 |
return self._wait( |
|
410 |
floatingip_id, |
|
411 |
current_status, get_status, delay, max_wait, wait_cb) |
Also available in: Unified diff