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
synnefo
Commits
c03a0b05
Commit
c03a0b05
authored
Jul 02, 2013
by
Christos Stavrakakis
Browse files
Merge branch 'feature-floating_ips+resize' into develop
parents
69b8685c
0a84aa41
Changes
47
Expand all
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
c03a0b05
...
...
@@ -40,3 +40,5 @@ snf-stats-app/synnefo_stats/version.py
astakosclient/astakosclient/version.py
snf-django-lib/snf_django/version.py
snf-branding/synnefo_branding/version.py
*.egg
*.tar.gz
Changelog
View file @
c03a0b05
...
...
@@ -6,6 +6,21 @@ Unified Changelog file for Synnefo versions >= 0.13
Since v0.13 most of the Synnefo components have been merged into a single
repository and have aligned versions.
.. _Changelog-0.14next:
v0.14next
=========
Released: UNRELEASED
Cyclades
--------
* Obsolete PUBLIC_USE_POOL setting, since Cyclades manages IP pool for all
type of networks.
Synnefo-wide
------------
.. _Changelog-0.14:
...
...
docs/scale/i-cyclades.rst
View file @
c03a0b05
...
...
@@ -42,7 +42,6 @@ In `/etc/synnefo/cyclades.conf` add:
.. code-block:: console
MAX_CIDR_BLOCK = 21
PUBLIC_USE_POOL = True
CPU_BAR_GRAPH_URL = 'https://cyclades.example.com/stats/%s/cpu-bar.png'
CPU_TIMESERIES_GRAPH_URL = 'https://cyclades.example.com/stats/%s/cpu-ts.png'
...
...
snf-cyclades-app/conf/20-snf-cyclades-app-api.conf
View file @
c03a0b05
...
...
@@ -17,6 +17,13 @@
## Network Configuration
##
#
## List of network IDs. All created instances will get a NIC connected to each
## network of this list. If the special network ID "SNF:ANY_PUBLIC" is used,
## Cyclades will automatically choose a public network and connect the server to
## it.
#DEFAULT_INSTANCE_NETWORKS=["SNF:ANY_PUBLIC"]
#
#
## Maximum allowed network size for private networks.
#MAX_CIDR_BLOCK = 22
#
...
...
@@ -24,12 +31,6 @@
#DEFAULT_MAC_PREFIX = 'aa:00:0'
#DEFAULT_BRIDGE = 'br0'
#
## Boolean value indicating whether synnefo would hold a Pool and allocate IP
## addresses. If this setting is set to False, IP pool management will be
## delegated to Ganeti. If machines have been created with this option as False,
## you must run network reconciliation after turning it to True.
#PUBLIC_USE_POOL = True
#
## Network flavors that users are allowed to create through API requests
#API_ENABLED_NETWORK_FLAVORS = ['MAC_FILTERED']
#
...
...
snf-cyclades-app/synnefo/api/actions.py
deleted
100644 → 0
View file @
69b8685c
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from
socket
import
getfqdn
from
vncauthproxy.client
import
request_forwarding
as
request_vnc_forwarding
from
django.db
import
transaction
from
django.conf
import
settings
from
django.http
import
HttpResponse
from
django.template.loader
import
render_to_string
from
django.utils
import
simplejson
as
json
from
snf_django.lib.api
import
faults
from
synnefo.api.util
import
(
random_password
,
get_vm
,
get_nic_from_index
,
get_network_free_address
)
from
synnefo.db.models
import
NetworkInterface
from
synnefo.db.pools
import
EmptyPool
from
synnefo.logic
import
backend
from
synnefo.logic.utils
import
get_rsapi_state
from
logging
import
getLogger
log
=
getLogger
(
__name__
)
server_actions
=
{}
network_actions
=
{}
def
server_action
(
name
):
'''Decorator for functions implementing server actions.
`name` is the key in the dict passed by the client.
'''
def
decorator
(
func
):
server_actions
[
name
]
=
func
return
func
return
decorator
def
network_action
(
name
):
'''Decorator for functions implementing network actions.
`name` is the key in the dict passed by the client.
'''
def
decorator
(
func
):
network_actions
[
name
]
=
func
return
func
return
decorator
@
server_action
(
'changePassword'
)
def
change_password
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# overLimit (413)
raise
faults
.
NotImplemented
(
'Changing password is not supported.'
)
@
server_action
(
'reboot'
)
def
reboot
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# overLimit (413)
log
.
info
(
"Reboot VM %s"
,
vm
)
reboot_type
=
args
.
get
(
'type'
,
''
)
if
reboot_type
not
in
(
'SOFT'
,
'HARD'
):
raise
faults
.
BadRequest
(
'Malformed Request.'
)
backend
.
reboot_instance
(
vm
,
reboot_type
.
lower
())
return
HttpResponse
(
status
=
202
)
@
server_action
(
'start'
)
def
start
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404)
log
.
info
(
"Start VM %s"
,
vm
)
if
args
:
raise
faults
.
BadRequest
(
'Malformed Request.'
)
backend
.
startup_instance
(
vm
)
return
HttpResponse
(
status
=
202
)
@
server_action
(
'shutdown'
)
def
shutdown
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404)
log
.
info
(
"Shutdown VM %s"
,
vm
)
if
args
:
raise
faults
.
BadRequest
(
'Malformed Request.'
)
backend
.
shutdown_instance
(
vm
)
return
HttpResponse
(
status
=
202
)
@
server_action
(
'rebuild'
)
def
rebuild
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# serverCapacityUnavailable (503),
# overLimit (413)
raise
faults
.
NotImplemented
(
'Rebuild not supported.'
)
@
server_action
(
'resize'
)
def
resize
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# serverCapacityUnavailable (503),
# overLimit (413),
# resizeNotAllowed (403)
raise
faults
.
NotImplemented
(
'Resize not supported.'
)
@
server_action
(
'confirmResize'
)
def
confirm_resize
(
request
,
vm
,
args
):
# Normal Response Code: 204
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# serverCapacityUnavailable (503),
# overLimit (413),
# resizeNotAllowed (403)
raise
faults
.
NotImplemented
(
'Resize not supported.'
)
@
server_action
(
'revertResize'
)
def
revert_resize
(
request
,
vm
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# serverCapacityUnavailable (503),
# overLimit (413),
# resizeNotAllowed (403)
raise
faults
.
NotImplemented
(
'Resize not supported.'
)
@
server_action
(
'console'
)
def
get_console
(
request
,
vm
,
args
):
"""Arrange for an OOB console of the specified type
This method arranges for an OOB console of the specified type.
Only consoles of type "vnc" are supported for now.
It uses a running instance of vncauthproxy to setup proper
VNC forwarding with a random password, then returns the necessary
VNC connection info to the caller.
"""
# Normal Response Code: 200
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# overLimit (413)
log
.
info
(
"Get console VM %s"
,
vm
)
console_type
=
args
.
get
(
'type'
,
''
)
if
console_type
!=
'vnc'
:
raise
faults
.
BadRequest
(
'Type can only be "vnc".'
)
# Use RAPI to get VNC console information for this instance
if
get_rsapi_state
(
vm
)
!=
'ACTIVE'
:
raise
faults
.
BadRequest
(
'Server not in ACTIVE state.'
)
if
settings
.
TEST
:
console_data
=
{
'kind'
:
'vnc'
,
'host'
:
'ganeti_node'
,
'port'
:
1000
}
else
:
console_data
=
backend
.
get_instance_console
(
vm
)
if
console_data
[
'kind'
]
!=
'vnc'
:
message
=
'got console of kind %s, not "vnc"'
%
console_data
[
'kind'
]
raise
faults
.
ServiceUnavailable
(
message
)
# Let vncauthproxy decide on the source port.
# The alternative: static allocation, e.g.
# sport = console_data['port'] - 1000
sport
=
0
daddr
=
console_data
[
'host'
]
dport
=
console_data
[
'port'
]
password
=
random_password
()
if
settings
.
TEST
:
fwd
=
{
'source_port'
:
1234
,
'status'
:
'OK'
}
else
:
fwd
=
request_vnc_forwarding
(
sport
,
daddr
,
dport
,
password
)
if
fwd
[
'status'
]
!=
"OK"
:
raise
faults
.
ServiceUnavailable
(
'vncauthproxy returned error status'
)
# Verify that the VNC server settings haven't changed
if
not
settings
.
TEST
:
if
console_data
!=
backend
.
get_instance_console
(
vm
):
raise
faults
.
ServiceUnavailable
(
'VNC Server settings changed.'
)
console
=
{
'type'
:
'vnc'
,
'host'
:
getfqdn
(),
'port'
:
fwd
[
'source_port'
],
'password'
:
password
}
if
request
.
serialization
==
'xml'
:
mimetype
=
'application/xml'
data
=
render_to_string
(
'console.xml'
,
{
'console'
:
console
})
else
:
mimetype
=
'application/json'
data
=
json
.
dumps
({
'console'
:
console
})
return
HttpResponse
(
data
,
mimetype
=
mimetype
,
status
=
200
)
@
server_action
(
'firewallProfile'
)
def
set_firewall_profile
(
request
,
vm
,
args
):
# Normal Response Code: 200
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# buildInProgress (409),
# overLimit (413)
profile
=
args
.
get
(
'profile'
,
''
)
log
.
info
(
"Set VM %s firewall %s"
,
vm
,
profile
)
if
profile
not
in
[
x
[
0
]
for
x
in
NetworkInterface
.
FIREWALL_PROFILES
]:
raise
faults
.
BadRequest
(
"Unsupported firewall profile"
)
backend
.
set_firewall_profile
(
vm
,
profile
)
return
HttpResponse
(
status
=
202
)
@
network_action
(
'add'
)
@
transaction
.
commit_on_success
def
add
(
request
,
net
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# buildInProgress (409),
# badMediaType(415),
# itemNotFound (404),
# overLimit (413)
if
net
.
state
!=
'ACTIVE'
:
raise
faults
.
BuildInProgress
(
'Network not active yet'
)
server_id
=
args
.
get
(
'serverRef'
,
None
)
if
not
server_id
:
raise
faults
.
BadRequest
(
'Malformed Request.'
)
vm
=
get_vm
(
server_id
,
request
.
user_uniq
,
non_suspended
=
True
)
address
=
None
if
net
.
dhcp
:
# Get a free IP from the address pool.
try
:
address
=
get_network_free_address
(
net
)
except
EmptyPool
:
raise
faults
.
OverLimit
(
'Network is full'
)
log
.
info
(
"Connecting VM %s to Network %s(%s)"
,
vm
,
net
,
address
)
backend
.
connect_to_network
(
vm
,
net
,
address
)
return
HttpResponse
(
status
=
202
)
@
network_action
(
'remove'
)
@
transaction
.
commit_on_success
def
remove
(
request
,
net
,
args
):
# Normal Response Code: 202
# Error Response Codes: computeFault (400, 500),
# serviceUnavailable (503),
# unauthorized (401),
# badRequest (400),
# badMediaType(415),
# itemNotFound (404),
# overLimit (413)
try
:
# attachment string: nic-<vm-id>-<nic-index>
server_id
=
args
.
get
(
'attachment'
,
None
).
split
(
'-'
)[
1
]
nic_index
=
args
.
get
(
'attachment'
,
None
).
split
(
'-'
)[
2
]
except
AttributeError
:
raise
faults
.
BadRequest
(
"Malformed Request"
)
except
IndexError
:
raise
faults
.
BadRequest
(
'Malformed Network Interface Id'
)
if
not
server_id
or
not
nic_index
:
raise
faults
.
BadRequest
(
'Malformed Request.'
)
vm
=
get_vm
(
server_id
,
request
.
user_uniq
,
non_suspended
=
True
)
nic
=
get_nic_from_index
(
vm
,
nic_index
)
log
.
info
(
"Removing NIC %s from VM %s"
,
str
(
nic
.
index
),
vm
)
if
nic
.
dirty
:
raise
faults
.
BuildInProgress
(
'Machine is busy.'
)
else
:
vm
.
nics
.
all
().
update
(
dirty
=
True
)
backend
.
disconnect_from_network
(
vm
,
nic
)
return
HttpResponse
(
status
=
202
)
snf-cyclades-app/synnefo/api/floating_ips.py
0 → 100644
View file @
c03a0b05
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from
django.conf.urls.defaults
import
patterns
from
django.db
import
transaction
from
django.http
import
HttpResponse
from
django.utils
import
simplejson
as
json
from
snf_django.lib
import
api
from
snf_django.lib.api
import
faults
,
utils
from
synnefo.api
import
util
from
synnefo
import
quotas
from
synnefo.db.models
import
Network
,
FloatingIP
from
logging
import
getLogger
log
=
getLogger
(
__name__
)
ips_urlpatterns
=
patterns
(
'synnefo.api.floating_ips'
,
(
r
'^(?:/|.json|.xml)?$'
,
'demux'
),
(
r
'^/(\w+)(?:.json|.xml)?$'
,
'floating_ip_demux'
),
)
pools_urlpatterns
=
patterns
(
"synnefo.api.floating_ips"
,
(
r
'^(?:/|.json|.xml)?$'
,
'list_floating_ip_pools'
),
)
def
demux
(
request
):
if
request
.
method
==
'GET'
:
return
list_floating_ips
(
request
)
elif
request
.
method
==
'POST'
:
return
allocate_floating_ip
(
request
)
else
:
return
api
.
method_not_allowed
(
request
)
def
floating_ip_demux
(
request
,
floating_ip_id
):
if
request
.
method
==
'GET'
:
return
get_floating_ip
(
request
,
floating_ip_id
)
elif
request
.
method
==
'DELETE'
:
return
release_floating_ip
(
request
,
floating_ip_id
)
else
:
return
api
.
method_not_allowed
(
request
)
def
ip_to_dict
(
floating_ip
):
machine_id
=
floating_ip
.
machine_id
return
{
"fixed_ip"
:
None
,
"id"
:
str
(
floating_ip
.
id
),
"instance_id"
:
str
(
machine_id
)
if
machine_id
else
None
,
"ip"
:
floating_ip
.
ipv4
,
"pool"
:
str
(
floating_ip
.
network_id
)}
@
api
.
api_method
(
http_method
=
"GET"
,
user_required
=
True
,
logger
=
log
,
serializations
=
[
"json"
])
def
list_floating_ips
(
request
):
"""Return user reserved floating IPs"""
log
.
debug
(
"list_floating_ips"
)
userid
=
request
.
user_uniq
floating_ips
=
FloatingIP
.
objects
.
filter
(
userid
=
userid
,
deleted
=
False
)
\
.
order_by
(
"id"
)
floating_ips
=
map
(
ip_to_dict
,
floating_ips
)
request
.
serialization
=
"json"
data
=
json
.
dumps
({
"floating_ips"
:
floating_ips
})
return
HttpResponse
(
data
,
status
=
200
)
@
api
.
api_method
(
http_method
=
"GET"
,
user_required
=
True
,
logger
=
log
,
serializations
=
[
"json"
])
def
get_floating_ip
(
request
,
floating_ip_id
):
"""Return information for a floating IP."""
userid
=
request
.
user_uniq
try
:
floating_ip
=
FloatingIP
.
objects
.
get
(
id
=
floating_ip_id
,
deleted
=
False
,
userid
=
userid
)
except
FloatingIP
.
DoesNotExist
:
raise
faults
.
ItemNotFound
(
"Floating IP '%s' does not exist"
%
floating_ip_id
)
request
.
serialization
=
"json"
data
=
json
.
dumps
({
"floating_ip"
:
ip_to_dict
(
floating_ip
)})
return
HttpResponse
(
data
,
status
=
200
)
@
api
.
api_method
(
http_method
=
'POST'
,
user_required
=
True
,
logger
=
log
,
serializations
=
[
"json"
])
@
transaction
.
commit_manually
def
allocate_floating_ip
(
request
):
"""Allocate a floating IP."""
req
=
utils
.
get_request_dict
(
request
)
log
.
info
(
'allocate_floating_ip %s'
,
req
)
userid
=
request
.
user_uniq
pool
=
req
.
get
(
"pool"
,
None
)
address
=
req
.
get
(
"address"
,
None
)
machine
=
None
net_objects
=
Network
.
objects
.
select_for_update
()
\
.
filter
(
public
=
True
,
floating_ip_pool
=
True
,
deleted
=
False
)
try
:
if
pool
is
None
:
# User did not specified a pool. Choose a random public IP
network
,
address
=
util
.
get_free_ip
(
net_objects
)
else
:
try
:
network_id
=
int
(
pool
)
except
ValueErrorx
:
raise
faults
.
BadRequest
(
"Invalid pool ID."
)
network
=
next
((
n
for
n
in
net_objects
if
n
.
id
==
pool
),
None
)
if
network
is
None
:
raise
faults
.
ItemNotFound
(
"Pool '%s' does not exist."
%
pool
)
if
address
is
None
:
# User did not specified an IP address. Choose a random one
# Gets X-Lock on IP pool
address
=
util
.
get_network_free_address
(
network
)
else
:
# User specified an IP address. Check that it is not a used
# floating IP
if
FloatingIP
.
objects
.
filter
(
network
=
network
,
deleted
=
False
,
ipv4
=
address
).
exists
():
msg
=
"Floating IP '%s' is reserved"
%
address
raise
faults
.
Conflict
(
msg
)
pool
=
network
.
get_pool
()
# Gets X-Lock
# Check address belongs to pool
if
not
pool
.
contains
(
address
):
raise
faults
.
BadRequest
(
"Invalid address"
)
if
pool
.
is_available
(
address
):
pool
.