Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
itminedu
kamaki
Commits
2e9ea066
Commit
2e9ea066
authored
Nov 29, 2013
by
Stavros Sachtouris
Browse files
Merge branch 'feature-network-api' into develop
parents
e3f54dc0
8ac8898f
Changes
9
Hide whitespace changes
Inline
Side-by-side
kamaki/cli/argument/__init__.py
View file @
2e9ea066
...
...
@@ -52,6 +52,7 @@ class Argument(object):
This is the top-level Argument class. It is suggested to extent this
class into more specific argument types.
"""
lvalue_delimiter
=
'/'
def
__init__
(
self
,
arity
,
help
=
None
,
parsed_name
=
None
,
default
=
None
):
self
.
arity
=
int
(
arity
)
...
...
@@ -86,6 +87,12 @@ class Argument(object):
*
self
.
parsed_name
,
dest
=
name
,
action
=
action
,
default
=
self
.
default
,
help
=
self
.
help
)
@
property
def
lvalue
(
self
):
"""A printable form of the left value when calling an argument e.g.,
--left-value=right-value"""
return
(
self
.
lvalue_delimiter
or
' '
).
join
(
self
.
parsed_name
or
[])
class
ConfigArgument
(
Argument
):
"""Manage a kamaki configuration (file)"""
...
...
kamaki/cli/commands/__init__.py
View file @
2e9ea066
...
...
@@ -263,7 +263,7 @@ class OutputFormatArgument(ValueArgument):
else
:
raise
CLIInvalidArgument
(
'Invalid value %s for argument %s'
%
(
newvalue
,
'/'
.
join
(
self
.
parsed_name
)
),
newvalue
,
self
.
lvalue
),
details
=
[
'Valid output formats: %s'
%
', '
.
join
(
self
.
formats
)])
...
...
kamaki/cli/commands/cyclades.py
View file @
2e9ea066
...
...
@@ -52,9 +52,7 @@ from kamaki.cli.commands import (
server_cmds
=
CommandTree
(
'server'
,
'Cyclades/Compute API server commands'
)
flavor_cmds
=
CommandTree
(
'flavor'
,
'Cyclades/Compute API flavor commands'
)
network_cmds
=
CommandTree
(
'network'
,
'Cyclades/Compute API network commands'
)
ip_cmds
=
CommandTree
(
'ip'
,
'Cyclades/Compute API floating ip commands'
)
_commands
=
[
server_cmds
,
flavor_cmds
,
network_cmds
]
_commands
=
[
server_cmds
,
flavor_cmds
]
about_authentication
=
'
\n
User Authentication:
\
...
...
@@ -415,13 +413,19 @@ class server_create(_init_cyclades, _optional_json, _server_wait):
'Connect server to network w. floating ip ( NETWORK_ID,IP )'
'(can be repeated)'
,
'--network-with-ip'
),
automatic_ip
=
FlagArgument
(
'Automatically assign an IP to the server'
,
'--automatic-ip'
)
)
required
=
(
'server_name'
,
'flavor_id'
,
'image_id'
)
@
errors
.
cyclades
.
cluster_size
def
_create_cluster
(
self
,
prefix
,
flavor_id
,
image_id
,
size
):
networks
=
[
dict
(
network
=
netid
)
for
netid
in
(
self
[
'network_id'
]
or
[])]
+
(
self
[
'network_id_and_ip'
]
or
[])
if
self
[
'automatic_ip'
]:
networks
=
[]
else
:
networks
=
[
dict
(
network
=
netid
)
for
netid
in
(
(
self
[
'network_id'
]
or
[])
+
(
self
[
'network_id_and_ip'
]
or
[])
)]
or
None
servers
=
[
dict
(
name
=
'%s%s'
%
(
prefix
,
i
if
size
>
1
else
''
),
flavor_id
=
flavor_id
,
...
...
@@ -473,6 +477,14 @@ class server_create(_init_cyclades, _optional_json, _server_wait):
def
main
(
self
):
super
(
self
.
__class__
,
self
).
_run
()
if
self
[
'automatic_ip'
]
and
(
self
[
'network_id'
]
or
self
[
'network_id_and_ip'
]):
raise
CLIInvalidArgument
(
'Invalid argument combination'
,
details
=
[
'Argument %s should not be combined with other'
%
(
self
.
arguments
[
'automatic_ip'
].
lvalue
),
'network-related arguments i.e., %s or %s'
%
(
self
.
arguments
[
'network_id'
].
lvalue
,
self
.
arguments
[
'network_id_and_ip'
].
lvalue
)])
self
.
_run
(
name
=
self
[
'server_name'
],
flavor_id
=
self
[
'flavor_id'
],
...
...
@@ -865,30 +877,3 @@ def _add_name(self, net):
net
[
'user_id'
]
+=
' (%s)'
%
usernames
[
user_id
]
if
tenant_id
:
net
[
'tenant_id'
]
+=
' (%s)'
%
usernames
[
tenant_id
]
@
command
(
network_cmds
)
class
network_wait
(
_init_cyclades
,
_network_wait
):
"""Wait for server to finish [PENDING, ACTIVE, DELETED]"""
arguments
=
dict
(
timeout
=
IntArgument
(
'Wait limit in seconds (default: 60)'
,
'--timeout'
,
default
=
60
)
)
@
errors
.
generic
.
all
@
errors
.
cyclades
.
connection
@
errors
.
cyclades
.
network_id
def
_run
(
self
,
network_id
,
current_status
):
net
=
self
.
client
.
get_network_details
(
network_id
)
if
net
[
'status'
].
lower
()
==
current_status
.
lower
():
self
.
_wait
(
network_id
,
current_status
,
timeout
=
self
[
'timeout'
])
else
:
self
.
error
(
'Network %s: Cannot wait for status %s, '
'status is already %s'
%
(
network_id
,
current_status
,
net
[
'status'
]))
def
main
(
self
,
network_id
,
current_status
=
'PENDING'
):
super
(
self
.
__class__
,
self
).
_run
()
self
.
_run
(
network_id
=
network_id
,
current_status
=
current_status
)
kamaki/cli/commands/network.py
View file @
2e9ea066
...
...
@@ -39,11 +39,13 @@ from kamaki.cli.command_tree import CommandTree
from
kamaki.cli.errors
import
(
CLISyntaxError
,
CLIBaseUrlError
,
CLIInvalidArgument
)
from
kamaki.clients.cyclades
import
CycladesNetworkClient
from
kamaki.cli.argument
import
FlagArgument
,
ValueArgument
,
RepeatableArgument
from
kamaki.cli.argument
import
(
FlagArgument
,
ValueArgument
,
RepeatableArgument
,
IntArgument
)
from
kamaki.cli.commands
import
_command_init
,
errors
,
addLogSettings
from
kamaki.cli.commands
import
(
_optional_output_cmd
,
_optional_json
,
_name_filter
,
_id_filter
)
from
kamaki.cli.utils
import
filter_dicts_by_dict
from
kamaki.cli.commands.cyclades
import
_service_wait
network_cmds
=
CommandTree
(
'network'
,
'Networking API network commands'
)
...
...
@@ -58,6 +60,30 @@ about_authentication = '\nUser Authentication:\
\n
* to set authentication token: /config set cloud.<cloud>.token <token>'
class
_network_wait
(
_service_wait
):
def
_wait
(
self
,
net_id
,
current_status
,
timeout
=
60
):
super
(
_network_wait
,
self
).
_wait
(
'Network'
,
net_id
,
self
.
client
.
wait_network
,
current_status
,
timeout
=
timeout
)
class
_port_wait
(
_service_wait
):
def
_wait
(
self
,
port_id
,
current_status
,
timeout
=
60
):
super
(
_port_wait
,
self
).
_wait
(
'Port'
,
port_id
,
self
.
client
.
wait_port
,
current_status
,
timeout
=
timeout
)
class
_port_wait
(
_service_wait
):
def
_wait
(
self
,
net_id
,
current_status
,
timeout
=
60
):
super
(
_network_wait
,
self
).
_wait
(
'Network'
,
net_id
,
self
.
client
.
wait_network
,
current_status
,
timeout
=
timeout
)
class
_init_network
(
_command_init
):
@
errors
.
generic
.
all
@
addLogSettings
...
...
@@ -164,7 +190,7 @@ class NetworkTypeArgument(ValueArgument):
@
command
(
network_cmds
)
class
network_create
(
_init_network
,
_optional_json
):
class
network_create
(
_init_network
,
_optional_json
,
_network_wait
):
"""Create a new network"""
arguments
=
dict
(
...
...
@@ -173,7 +199,8 @@ class network_create(_init_network, _optional_json):
'Make network shared (special privileges required)'
,
'--shared'
),
network_type
=
NetworkTypeArgument
(
'Valid network types: %s'
%
(
', '
.
join
(
NetworkTypeArgument
.
types
)),
'--type'
)
'--type'
),
wait
=
FlagArgument
(
'Wait network to build'
,
(
'-w'
,
'--wait'
)),
)
required
=
(
'network_type'
,
)
...
...
@@ -183,6 +210,8 @@ class network_create(_init_network, _optional_json):
def
_run
(
self
,
network_type
):
net
=
self
.
client
.
create_network
(
network_type
,
name
=
self
[
'name'
],
shared
=
self
[
'shared'
])
if
self
[
'wait'
]:
self
.
_wait
(
net
[
'id'
],
net
[
'status'
])
self
.
_print
(
net
,
self
.
print_dict
)
def
main
(
self
):
...
...
@@ -225,6 +254,33 @@ class network_modify(_init_network, _optional_json):
self
.
_run
(
network_id
=
network_id
)
@
command
(
network_cmds
)
class
network_wait
(
_init_network
,
_network_wait
):
"""Wait for network to finish [PENDING, ACTIVE, DELETED]"""
arguments
=
dict
(
timeout
=
IntArgument
(
'Wait limit in seconds (default: 60)'
,
'--timeout'
,
default
=
60
)
)
@
errors
.
generic
.
all
@
errors
.
cyclades
.
connection
@
errors
.
cyclades
.
network_id
def
_run
(
self
,
network_id
,
current_status
):
net
=
self
.
client
.
get_network_details
(
network_id
)
if
net
[
'status'
].
lower
()
==
current_status
.
lower
():
self
.
_wait
(
network_id
,
current_status
,
timeout
=
self
[
'timeout'
])
else
:
self
.
error
(
'Network %s: Cannot wait for status %s, '
'status is already %s'
%
(
network_id
,
current_status
,
net
[
'status'
]))
def
main
(
self
,
network_id
,
current_status
=
'PENDING'
):
super
(
self
.
__class__
,
self
).
_run
()
self
.
_run
(
network_id
=
network_id
,
current_status
=
current_status
)
@
command
(
subnet_cmds
)
class
subnet_list
(
_init_network
,
_optional_json
,
_name_filter
,
_id_filter
):
"""List subnets
...
...
@@ -432,7 +488,7 @@ class port_modify(_init_network, _optional_json):
@
command
(
port_cmds
)
class
port_create
(
_init_network
,
_optional_json
):
class
port_create
(
_init_network
,
_optional_json
,
_port_wait
):
"""Create a new port (== connect server to network)"""
arguments
=
dict
(
...
...
@@ -448,7 +504,8 @@ class port_create(_init_network, _optional_json):
network_id
=
ValueArgument
(
'Set the network ID'
,
'--network-id'
),
device_id
=
ValueArgument
(
'The device is either a virtual server or a virtual router'
,
'--device-id'
)
'--device-id'
),
wait
=
FlagArgument
(
'Wait port to be established'
,
(
'-w'
,
'--wait'
)),
)
required
=
(
'network_id'
,
'device_id'
)
...
...
@@ -464,6 +521,8 @@ class port_create(_init_network, _optional_json):
name
=
self
[
'name'
],
security_groups
=
self
[
'security_group_id'
],
fixed_ips
=
fixed_ips
)
if
self
[
'wait'
]:
self
.
_wait
(
r
[
'id'
],
r
[
'status'
])
self
.
_print
(
r
,
self
.
print_dict
)
def
main
(
self
):
...
...
@@ -471,6 +530,32 @@ class port_create(_init_network, _optional_json):
self
.
_run
(
network_id
=
self
[
'network_id'
],
device_id
=
self
[
'device_id'
])
@
command
(
port_cmds
)
class
port_wait
(
_init_network
,
_port_wait
):
"""Wait for port to finish [PENDING, ACTIVE, DELETED]"""
arguments
=
dict
(
timeout
=
IntArgument
(
'Wait limit in seconds (default: 60)'
,
'--timeout'
,
default
=
60
)
)
@
errors
.
generic
.
all
@
errors
.
cyclades
.
connection
def
_run
(
self
,
port_id
,
current_status
):
port
=
self
.
client
.
get_port_details
(
port_id
)
if
port
[
'status'
].
lower
()
==
current_status
.
lower
():
self
.
_wait
(
port_id
,
current_status
,
timeout
=
self
[
'timeout'
])
else
:
self
.
error
(
'Port %s: Cannot wait for status %s, '
'status is already %s'
%
(
port_id
,
current_status
,
port
[
'status'
]))
def
main
(
self
,
port_id
,
current_status
=
'PENDING'
):
super
(
self
.
__class__
,
self
).
_run
()
self
.
_run
(
port_id
=
port_id
,
current_status
=
current_status
)
@
command
(
ip_cmds
)
class
ip_list
(
_init_network
,
_optional_json
):
"""List reserved floating IPs"""
...
...
@@ -492,7 +577,8 @@ class ip_info(_init_network, _optional_json):
@
errors
.
generic
.
all
@
errors
.
cyclades
.
connection
def
_run
(
self
,
ip_id
):
self
.
_print
(
self
.
client
.
get_floatingip_details
(
ip_id
))
self
.
_print
(
self
.
client
.
get_floatingip_details
(
ip_id
),
self
.
print_dict
)
def
main
(
self
,
ip_id
):
super
(
self
.
__class__
,
self
).
_run
()
...
...
@@ -529,7 +615,7 @@ class ip_delete(_init_network, _optional_output_cmd):
"""Unreserve an IP (also delete the port, if attached)"""
def
_run
(
self
,
ip_id
):
self
.
_optional_output
(
self
.
client
.
floatingip
_delete
(
ip_id
))
self
.
_optional_output
(
self
.
client
.
delete_
floatingip
(
ip_id
))
def
main
(
self
,
ip_id
):
super
(
self
.
__class__
,
self
).
_run
()
...
...
kamaki/cli/commands/pithos.py
View file @
2e9ea066
...
...
@@ -377,14 +377,14 @@ class file_modify(_pithos_container):
if
self
[
'publish'
]
and
self
[
'unpublish'
]:
raise
CLIInvalidArgument
(
'Arguments %s and %s cannot be used together'
%
(
'/'
.
join
(
self
.
arguments
[
'publish'
].
parsed_name
)
,
'/'
.
join
(
self
.
arguments
[
'publish'
].
parsed_name
)
))
self
.
arguments
[
'publish'
].
lvalue
,
self
.
arguments
[
'publish'
].
lvalue
))
if
self
[
'no_permissions'
]
and
(
self
[
'uuid_for_read_permission'
]
or
self
[
'uuid_for_write_permission'
]):
raise
CLIInvalidArgument
(
'%s cannot be used with other permission arguments'
%
'/'
.
join
(
self
.
arguments
[
'no_permissions'
].
parsed_nam
e
))
'%s cannot be used with other permission arguments'
%
(
self
.
arguments
[
'no_permissions'
].
lvalu
e
))
self
.
_run
()
...
...
@@ -555,8 +555,8 @@ class _source_destination(_pithos_container, _optional_output_cmd):
self
.
dst_client
.
account
,
self
.
dst_client
.
container
,
dst_path
),
'Use %s to transfer overwrite'
%
(
'/'
.
join
(
self
.
arguments
[
'force'
].
parsed_name
)
)])
'Use %s to transfer overwrite'
%
(
self
.
arguments
[
'force'
].
lvalue
)])
else
:
# One object transfer
try
:
...
...
@@ -570,8 +570,7 @@ class _source_destination(_pithos_container, _optional_output_cmd):
'Missing specific path container %s'
%
self
.
container
,
importance
=
2
,
details
=
[
'To transfer container contents %s'
%
(
'/'
.
join
(
self
.
arguments
[
'source_prefix'
].
parsed_name
))])
self
.
arguments
[
'source_prefix'
].
lvalue
)])
raise
dst_path
=
self
.
dst_path
or
self
.
path
dst_obj
=
dst_objects
.
get
(
dst_path
or
self
.
path
,
None
)
...
...
@@ -589,8 +588,7 @@ class _source_destination(_pithos_container, _optional_output_cmd):
self
.
container
,
self
.
path
),
'To recursively copy a directory, use'
,
' %s'
%
(
'/'
.
join
(
self
.
arguments
[
'source_prefix'
].
parsed_name
)),
' %s'
%
self
.
arguments
[
'source_prefix'
].
lvalue
,
'To create a file, use'
,
' /file create (general purpose)'
,
' /file mkdir (a directory object)'
])
...
...
@@ -607,8 +605,8 @@ class _source_destination(_pithos_container, _optional_output_cmd):
self
.
dst_client
.
account
,
self
.
dst_client
.
container
,
dst_path
),
'Use %s to transfer overwrite'
%
(
'/'
.
join
(
self
.
arguments
[
'force'
].
parsed_name
)
)])
'Use %s to transfer overwrite'
%
(
self
.
arguments
[
'force'
].
lvalue
)])
return
pairs
def
_run
(
self
,
source_path_or_url
,
destination_path_or_url
=
''
):
...
...
@@ -873,8 +871,8 @@ class file_upload(_pithos_container, _optional_output_cmd):
if
path
.
isdir
(
lpath
):
if
not
self
[
'recursive'
]:
raise
CLIError
(
'%s is a directory'
%
lpath
,
details
=
[
'Use %s to upload directories & contents'
%
'/'
.
join
(
self
.
arguments
[
'recursive'
].
parsed_nam
e
)])
'Use %s to upload directories & contents'
%
(
self
.
arguments
[
'recursive'
].
lvalu
e
)])
robj
=
self
.
client
.
container_get
(
path
=
rpath
)
if
not
self
[
'overwrite'
]:
if
robj
.
json
:
...
...
@@ -1174,18 +1172,18 @@ class file_download(_pithos_container):
elif
path
.
exists
(
lpath
):
raise
CLIError
(
'Cannot overwrite %s'
%
lpath
,
details
=
[
'To overwrite/resume, use %s'
%
'/'
.
join
(
self
.
arguments
[
'resume'
].
parsed_nam
e
)])
details
=
[
'To overwrite/resume, use %s'
%
(
self
.
arguments
[
'resume'
].
lvalu
e
)])
else
:
ret
.
append
((
opath
,
lpath
,
None
))
elif
self
.
path
:
raise
CLIError
(
'Remote object /%s/%s is a directory'
%
(
self
.
container
,
local_path
),
details
=
[
'Use %s to download directories'
%
'/'
.
join
(
self
.
arguments
[
'recursive'
].
parsed_nam
e
)])
details
=
[
'Use %s to download directories'
%
(
self
.
arguments
[
'recursive'
].
lvalu
e
)])
else
:
parsed_name
=
'/'
.
join
(
self
.
arguments
[
'recursive'
].
parsed_name
)
parsed_name
=
self
.
arguments
[
'recursive'
].
lvalue
raise
CLIError
(
'Cannot download container %s'
%
self
.
container
,
details
=
[
...
...
@@ -1197,8 +1195,8 @@ class file_download(_pithos_container):
if
path
.
exists
(
local_path
)
and
not
self
[
'resume'
]:
raise
CLIError
(
'Cannot overwrite local file %s'
%
(
lpath
),
details
=
[
'To overwrite/resume, use %s'
%
'/'
.
join
(
self
.
arguments
[
'resume'
].
parsed_nam
e
)])
details
=
[
'To overwrite/resume, use %s'
%
(
self
.
arguments
[
'resume'
].
lvalu
e
)])
ret
.
append
((
rpath
,
local_path
,
self
[
'resume'
]))
for
r
,
l
,
resume
in
ret
:
if
r
:
...
...
@@ -1528,8 +1526,8 @@ class container_delete(_pithos_account):
delimiter
,
msg
=
'/'
,
'Empty and d%s'
%
msg
[
1
:]
elif
num_of_contents
:
raise
CLIError
(
'Container %s is not empty'
%
container
,
details
=
[
'Use %s to delete non-empty containers'
%
'/'
.
join
(
self
.
arguments
[
'recursive'
].
parsed_nam
e
)])
'Use %s to delete non-empty containers'
%
(
self
.
arguments
[
'recursive'
].
lvalu
e
)])
if
self
[
'yes'
]
or
self
.
ask_user
(
msg
):
if
num_of_contents
:
self
.
client
.
del_container
(
delimiter
=
delimiter
)
...
...
@@ -1658,8 +1656,8 @@ class group_create(_pithos_group, _optional_json):
else
:
raise
CLISyntaxError
(
'No valid users specified, use %s or %s'
%
(
'/'
.
join
(
self
.
arguments
[
'user_uuid'
].
parsed_name
)
,
'/'
.
join
(
self
.
arguments
[
'username'
].
parsed_name
)
),
self
.
arguments
[
'user_uuid'
].
lvalue
,
self
.
arguments
[
'username'
].
lvalue
),
details
=
[
'Check if a username or uuid is valid with'
,
' user uuid2username'
,
'OR'
,
' user username2uuid'
])
...
...
kamaki/clients/__init__.py
View file @
2e9ea066
...
...
@@ -492,3 +492,61 @@ class Client(Logged):
def
move
(
self
,
path
,
**
kwargs
):
return
self
.
request
(
'move'
,
path
,
**
kwargs
)
class
Waiter
(
object
):
def
_wait
(
self
,
item_id
,
current_status
,
get_status
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
"""Wait for item while its status is current_status
:param server_id: integer (str or int)
:param current_status: (str)
:param get_status: (method(self, item_id)) if called, returns
(status, progress %) If no way to tell progress, return None
:param delay: time interval between retries
:param wait_cb: (method(total steps)) returns a generator for
reporting progress or timeouts i.e., for a progress bar
:returns: (str) the new mode if successful, (bool) False if timed out
"""
status
,
progress
=
get_status
(
self
,
item_id
)
if
wait_cb
:
wait_gen
=
wait_cb
(
max_wait
//
delay
)
wait_gen
.
next
()
if
status
!=
current_status
:
if
wait_cb
:
try
:
wait_gen
.
next
()
except
Exception
:
pass
return
status
old_wait
=
total_wait
=
0
while
status
==
current_status
and
total_wait
<=
max_wait
:
if
wait_cb
:
try
:
for
i
in
range
(
total_wait
-
old_wait
):
wait_gen
.
next
()
except
Exception
:
break
old_wait
=
total_wait
total_wait
=
progress
or
total_wait
+
1
sleep
(
delay
)
status
,
progress
=
get_status
(
self
,
item_id
)
if
total_wait
<
max_wait
:
if
wait_cb
:
try
:
for
i
in
range
(
max_wait
):
wait_gen
.
next
()
except
:
pass
return
status
if
status
!=
current_status
else
False
kamaki/clients/compute/__init__.py
View file @
2e9ea066
...
...
@@ -128,6 +128,14 @@ class ComputeClient(ComputeRestClient):
:param personality: a list of (file path, file contents) tuples,
describing files to be injected into virtual server upon creation
:param networks: (list of dicts) Networks to connect to, list this:
"networks": [
{"network": <network_uuid>},
{"network": <network_uuid>, "fixed_ip": address},
{"port": <port_id>}, ...]
ATTENTION: Empty list is different to None. None means ' do not
mention it', empty list means 'automatically get an ip'
:returns: a dict with the new virtual server details
:raises ClientError: wraps request errors
...
...
@@ -141,8 +149,8 @@ class ComputeClient(ComputeRestClient):
if
personality
:
req
[
'server'
][
'personality'
]
=
personality
if
networks
:
req
[
'server'
][
'networks'
]
=
networks
if
networks
is
not
None
:
req
[
'server'
][
'networks'
]
=
networks
or
[]
r
=
self
.
servers_post
(
json_data
=
req
,
...
...
kamaki/clients/cyclades/__init__.py
View file @
2e9ea066
...
...
@@ -31,15 +31,13 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from
time
import
sleep
from
kamaki.clients.cyclades.rest_api
import
CycladesRestClient
from
kamaki.clients.network
import
NetworkClient
from
kamaki.clients.utils
import
path4url
from
kamaki.clients
import
ClientError
from
kamaki.clients
import
ClientError
,
Waiter
class
CycladesClient
(
CycladesRestClient
):
class
CycladesClient
(
CycladesRestClient
,
Waiter
):
"""Synnefo Cyclades Compute API client"""
def
create_server
(
...
...
@@ -63,6 +61,8 @@ class CycladesClient(CycladesRestClient):
{"network": <network_uuid>},
{"network": <network_uuid>, "fixed_ip": address},
{"port": <port_id>}, ...]
ATTENTION: Empty list is different to None. None means ' do not
mention it', empty list means 'automatically get an ip'
:returns: a dict with the new virtual server details
...
...
@@ -78,7 +78,7 @@ class CycladesClient(CycladesRestClient):
return
super
(
CycladesClient
,
self
).
create_server
(
name
,
flavor_id
,
image_id
,
metadata
=
metadata
,
personality
=
personality
)
metadata
=
metadata
,
personality
=
personality
,
networks
=
networks
)
def
start_server
(
self
,
server_id
):
"""Submit a startup request
...
...
@@ -159,184 +159,6 @@ class CycladesClient(CycladesRestClient):
r
=
self
.
servers_stats_get
(
server_id
)
return
r
.
json
[
'stats'
]
def
list_networks
(
self
,
detail
=
False
):
"""
:param detail: (bool)
:returns: (list) id,name if not detail else full info per network
"""
detail
=
'detail'
if
detail
else
''
r
=
self
.
networks_get
(
command
=
detail
)
return
r
.
json
[
'networks'
]
def
list_network_nics
(
self
,
network_id
):
"""
:param network_id: integer (str or int)
:returns: (list)
"""
r
=
self
.
networks_get
(
network_id
=
network_id
)
return
r
.
json
[
'network'
][
'attachments'
]
def
create_network
(
self
,
name
,
cidr
=
None
,
gateway
=
None
,
type
=
None
,
dhcp
=
False
):
"""
:param name: (str)
:param cidr: (str)
:param geteway: (str)
:param type: (str) if None, will use MAC_FILTERED as default
Valid values: CUSTOM, IP_LESS_ROUTED, MAC_FILTERED, PHYSICAL_VLAN
:param dhcp: (bool)
:returns: (dict) network detailed info
"""
net
=
dict
(
name
=
name
)
if
cidr
:
net
[
'cidr'
]
=
cidr
if
gateway
:
net
[
'gateway'
]
=
gateway
net
[
'type'
]
=
type
or
'MAC_FILTERED'
net
[
'dhcp'
]
=
True
if
dhcp
else
False
req
=
dict
(
network
=
net
)
r
=
self
.
networks_post
(
json_data
=
req
,
success
=
202
)
return
r
.
json
[
'network'
]
def
get_network_details
(
self
,
network_id
):
"""
:param network_id: integer (str or int)
:returns: (dict)
"""
r
=
self
.
networks_get
(
network_id
=
network_id
)
return
r
.
json
[
'network'
]
def
update_network_name
(
self
,
network_id
,
new_name
):
"""
:param network_id: integer (str or int)
:param new_name: (str)
:returns: (dict) response headers
"""
req
=
{
'network'
:
{
'name'
:
new_name
}}