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
eb647cfe
Commit
eb647cfe
authored
Nov 29, 2013
by
Stavros Sachtouris
Browse files
Allow ports without device_id in lib + waits
Refs: #4624, #4563
parent
c6afee48
Changes
7
Hide whitespace changes
Inline
Side-by-side
kamaki/cli/argument/__init__.py
View file @
eb647cfe
...
...
@@ -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 @
eb647cfe
...
...
@@ -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 @
eb647cfe
...
...
@@ -415,13 +415,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 +479,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'
],
...
...
kamaki/cli/commands/pithos.py
View file @
eb647cfe
...
...
@@ -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/compute/__init__.py
View file @
eb647cfe
...
...
@@ -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 @
eb647cfe
...
...
@@ -61,6 +61,8 @@ class CycladesClient(CycladesRestClient, Waiter):
{"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
...
...
@@ -76,7 +78,7 @@ class CycladesClient(CycladesRestClient, Waiter):
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
...
...
@@ -157,130 +159,6 @@ class CycladesClient(CycladesRestClient, Waiter):
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
}}
r
=
self
.
networks_put
(
network_id
=
network_id
,
json_data
=
req
)
return
r
.
headers
def
delete_network
(
self
,
network_id
):
"""
:param network_id: integer (str or int)
:returns: (dict) response headers
:raises ClientError: 421 Network in use
"""
try
:
r
=
self
.
networks_delete
(
network_id
)
return
r
.
headers
except
ClientError
as
err
:
if
err
.
status
==
421
:
err
.
details
=
[
'Network may be still connected to at least one server'
]
raise
def
connect_server
(
self
,
server_id
,
network_id
):
""" Connect a server to a network
:param server_id: integer (str or int)
:param network_id: integer (str or int)
:returns: (dict) response headers
"""
req
=
{
'add'
:
{
'serverRef'
:
server_id
}}
r
=
self
.
networks_post
(
network_id
,
'action'
,
json_data
=
req
)
return
r
.
headers
def
disconnect_server
(
self
,
server_id
,
nic_id
):
"""
:param server_id: integer (str or int)
:param nic_id: (str)
:returns: (int) the number of nics disconnected
"""
vm_nets
=
self
.
list_server_nics
(
server_id
)
num_of_disconnections
=
0
for
(
nic_id
,
network_id
)
in
[(
net
[
'id'
],
net
[
'network_id'
])
for
net
in
vm_nets
if
nic_id
==
net
[
'id'
]]:
req
=
{
'remove'
:
{
'attachment'
:
'%s'
%
nic_id
}}
self
.
networks_post
(
network_id
,
'action'
,
json_data
=
req
)
num_of_disconnections
+=
1
return
num_of_disconnections
def
disconnect_network_nics
(
self
,
netid
):
"""
:param netid: integer (str or int)
"""
for
nic
in
self
.
list_network_nics
(
netid
):
req
=
dict
(
remove
=
dict
(
attachment
=
nic
))
self
.
networks_post
(
netid
,
'action'
,
json_data
=
req
)
def
wait_server
(
self
,
server_id
,
current_status
=
'BUILD'
,
...
...
@@ -308,31 +186,6 @@ class CycladesClient(CycladesRestClient, Waiter):
return
self
.
_wait
(
server_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_network
(
self
,
net_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
"""Wait for network while its status is current_status
:param net_id: integer (str or int)
:param current_status: (str) PENDING | ACTIVE | DELETED
:param delay: time interval between retries
:max_wait: (int) timeout in secconds
:param wait_cb: if set a progressbar is used to show progress
:returns: (str) the new mode if succesfull, (bool) False if timed out
"""
def
get_status
(
self
,
net_id
):
r
=
self
.
get_network_details
(
net_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
net_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_firewall
(
self
,
server_id
,
current_status
=
'DISABLED'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
...
...
@@ -358,7 +211,7 @@ class CycladesClient(CycladesRestClient, Waiter):
server_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
class
CycladesNetworkClient
(
NetworkClient
,
Waiter
):
class
CycladesNetworkClient
(
NetworkClient
):
"""Cyclades Network API extentions"""
network_types
=
(
...
...
@@ -379,14 +232,16 @@ class CycladesNetworkClient(NetworkClient, Waiter):
return
r
.
json
[
'network'
]
def
create_port
(
self
,
network_id
,
device_id
,
security_groups
=
None
,
name
=
None
,
fixed_ips
=
None
):
port
=
dict
(
network_id
=
network_id
,
device_id
=
device_id
)
self
,
network_id
,
device_id
=
None
,
security_groups
=
None
,
name
=
None
,
fixed_ips
=
None
):
port
=
dict
(
network_id
=
network_id
)
if
device_id
:
port
[
'device_id'
]
=
device_id
if
security_groups
:
port
[
'security_groups'
]
=
security_groups
if
name
:
port
[
'name'
]
=
name
for
fixed_ip
in
fixed_ips
:
for
fixed_ip
in
fixed_ips
or
[]
:
diff
=
set
([
'subnet_id'
,
'ip_address'
]).
difference
(
fixed_ip
)
if
diff
:
raise
ValueError
(
...
...
@@ -396,24 +251,11 @@ class CycladesNetworkClient(NetworkClient, Waiter):
r
=
self
.
ports_post
(
json_data
=
dict
(
port
=
port
),
success
=
201
)
return
r
.
json
[
'port'
]
def
wait_network
(
self
,
net_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
create_floatingip
(
self
,
floating_network_id
,
floating_ip_address
=
''
):
return
super
(
CycladesNetworkClient
,
self
).
create_floatingip
(
floating_network_id
,
floating_ip_address
=
floating_ip_address
)
def
get_status
(
self
,
net_id
):
r
=
self
.
get_network_details
(
net_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
net_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_port
(
self
,
port_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
get_status
(
self
,
net_id
):
r
=
self
.
get_port_details
(
port_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
port_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
update_floatingip
(
self
,
floating_network_id
,
floating_ip_address
=
''
):
"""To nullify something optional, use None"""
return
super
(
CycladesNetworkClient
,
self
).
update_floatingip
(
floating_network_id
,
floating_ip_address
=
floating_ip_address
)
kamaki/clients/network/__init__.py
View file @
eb647cfe
...
...
@@ -31,11 +31,11 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from
kamaki.clients
import
ClientError
from
kamaki.clients
import
ClientError
,
Waiter
from
kamaki.clients.network.rest_api
import
NetworkRestClient
class
NetworkClient
(
NetworkRestClient
):
class
NetworkClient
(
NetworkRestClient
,
Waiter
):
"""OpenStack Network API 2.0 client"""
def
list_networks
(
self
):
...
...
@@ -362,3 +362,50 @@ class NetworkClient(NetworkRestClient):
def
delete_floatingip
(
self
,
floatingip_id
):
r
=
self
.
floatingips_delete
(
floatingip_id
,
success
=
204
)
return
r
.
headers
# Wait methods
def
wait_network
(
self
,
net_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
get_status
(
self
,
net_id
):
r
=
self
.
get_network_details
(
net_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
net_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_subnet
(
self
,
subnet_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
get_status
(
self
,
subnet_id
):
r
=
self
.
get_subnet_details
(
subnet_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
subnet_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_port
(
self
,
port_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
get_status
(
self
,
net_id
):
r
=
self
.
get_port_details
(
port_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
port_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
def
wait_floatingip
(
self
,
floatingip_id
,
current_status
=
'PENDING'
,
delay
=
1
,
max_wait
=
100
,
wait_cb
=
None
):
def
get_status
(
self
,
floatingip_id
):
r
=
self
.
get_network_details
(
floatingip_id
)
return
r
[
'status'
],
None
return
self
.
_wait
(
floatingip_id
,
current_status
,
get_status
,
delay
,
max_wait
,
wait_cb
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment