Revision 7fede91e
b/snf-cyclades-app/synnefo/api/management/commands/network-create.py | ||
---|---|---|
35 | 35 |
|
36 | 36 |
from django.core.management.base import BaseCommand, CommandError |
37 | 37 |
|
38 |
from synnefo.db.models import Network |
|
38 |
from synnefo.db.models import Network, Backend
|
|
39 | 39 |
from synnefo.api.util import network_link_from_type |
40 | 40 |
from synnefo.logic.backend import create_network |
41 | 41 |
|
... | ... | |
88 | 88 |
dest='gateway6', |
89 | 89 |
default=None, |
90 | 90 |
help='IPv6 gateway of the network'), |
91 |
make_option('--backend-id', |
|
92 |
dest='backend_id', |
|
93 |
default=None, |
|
94 |
help='ID of the backend that the network will be created. Only for' |
|
95 |
' public networks') |
|
91 | 96 |
) |
92 | 97 |
|
93 | 98 |
def handle(self, *args, **options): |
... | ... | |
97 | 102 |
name = options['name'] |
98 | 103 |
subnet = options['subnet'] |
99 | 104 |
typ = options['type'] |
105 |
backend_id = options['backend_id'] |
|
106 |
public = options['public'] |
|
100 | 107 |
|
101 | 108 |
if not name: |
102 | 109 |
raise CommandError("Name is required") |
103 | 110 |
if not subnet: |
104 | 111 |
raise CommandError("Subnet is required") |
112 |
if public and not backend_id: |
|
113 |
raise CommandError("backend-id is required") |
|
114 |
if backend_id and not public: |
|
115 |
raise CommandError("Private networks must be created to" |
|
116 |
" all backends") |
|
117 |
|
|
118 |
if backend_id: |
|
119 |
try: |
|
120 |
backend_id = int(backend_id) |
|
121 |
backend = Backend.objects.get(id=backend_id) |
|
122 |
except ValueError: |
|
123 |
raise CommandError("Invalid backend ID") |
|
124 |
except Backend.DoesNotExist: |
|
125 |
raise CommandError("Backend not found in DB") |
|
105 | 126 |
|
106 | 127 |
link = network_link_from_type(typ) |
107 | 128 |
|
... | ... | |
117 | 138 |
gateway=gateway, |
118 | 139 |
dhcp=options['dhcp'], |
119 | 140 |
type=options['type'], |
120 |
public=options['public'],
|
|
141 |
public=public,
|
|
121 | 142 |
link=link, |
122 | 143 |
gateway6=gateway6, |
123 | 144 |
subnet6=subnet6, |
124 | 145 |
state='PENDING') |
125 | 146 |
|
126 |
# Create BackendNetwork entries for each Backend |
|
127 |
network.create_backend_network() |
|
147 |
if public: |
|
148 |
# Create BackendNetwork only to the specified Backend |
|
149 |
network.create_backend_network(backend) |
|
150 |
create_network(network, backends=[backend]) |
|
151 |
else: |
|
152 |
# Create BackendNetwork entries for all Backends |
|
153 |
network.create_backend_network() |
|
154 |
create_network(network) |
|
128 | 155 |
|
129 |
# Create the network to the backends |
|
130 |
create_network(network) |
|
131 | 156 |
|
132 | 157 |
|
133 | 158 |
def validate_network_info(options): |
b/snf-cyclades-app/synnefo/api/management/commands/network-remove.py | ||
---|---|---|
1 |
# Copyright 2011-2012 GRNET S.A. All rights reserved. |
|
2 |
# |
|
3 |
# Redistribution and use in source and binary forms, with or without |
|
4 |
# modification, are permitted provided that the following conditions |
|
5 |
# are met: |
|
6 |
# |
|
7 |
# 1. Redistributions of source code must retain the above copyright |
|
8 |
# notice, this list of conditions and the following disclaimer. |
|
9 |
# |
|
10 |
# 2. Redistributions in binary form must reproduce the above copyright |
|
11 |
# notice, this list of conditions and the following disclaimer in the |
|
12 |
# documentation and/or other materials provided with the distribution. |
|
13 |
# |
|
14 |
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
15 |
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
16 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
17 |
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
18 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
19 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
20 |
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
21 |
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
22 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
23 |
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
24 |
# SUCH DAMAGE. |
|
25 |
# |
|
26 |
# The views and conclusions contained in the software and documentation are |
|
27 |
# those of the authors and should not be interpreted as representing official |
|
28 |
# policies, either expressed or implied, of GRNET S.A. |
|
29 |
# |
|
30 |
|
|
31 |
from django.core.management.base import BaseCommand, CommandError |
|
32 |
from synnefo.db.models import Network |
|
33 |
from synnefo.api.networks import delete_network as api_delete_network |
|
34 |
from synnefo.logic.backend import delete_network as backend_delete_network |
|
35 |
|
|
36 |
|
|
37 |
class Command(BaseCommand): |
|
38 |
can_import_settings = True |
|
39 |
|
|
40 |
help = "Remove a network from the Database, and Ganeti" |
|
41 |
|
|
42 |
output_transaction = True # The management command runs inside |
|
43 |
# an SQL transaction |
|
44 |
|
|
45 |
def handle(self, *args, **options): |
|
46 |
if len(args) < 1: |
|
47 |
raise CommandError("Please provide a network ID") |
|
48 |
|
|
49 |
try: |
|
50 |
network_id = int(args[0]) |
|
51 |
network = Network.objects.get(id=network_id) |
|
52 |
except ValueError: |
|
53 |
raise CommandError("Invalid network ID") |
|
54 |
except network.DoesNotExist: |
|
55 |
raise CommandError("Network not found in DB") |
|
56 |
|
|
57 |
self.stdout.write('Trying to remove network: %s\n' % str(network)) |
|
58 |
|
|
59 |
if network.machines.exists(): |
|
60 |
raise CommandError('Network is not empty. Can not delete') |
|
61 |
|
|
62 |
if network.public: |
|
63 |
network.action = 'DESTROY' |
|
64 |
backend_delete_network(network) |
|
65 |
else: |
|
66 |
api_delete_network(network) |
|
67 |
|
|
68 |
self.stdout.write('Successfully removed network.\n') |
b/snf-cyclades-app/synnefo/api/networks.py | ||
---|---|---|
47 | 47 |
from synnefo.api.faults import (BadRequest, Unauthorized, |
48 | 48 |
NetworkInUse, OverLimit) |
49 | 49 |
from synnefo.db.models import Network, Pool |
50 |
|
|
51 | 50 |
from synnefo.logic import backend |
52 | 51 |
from synnefo.settings import MAX_CIDR_BLOCK |
53 | 52 |
|
... | ... | |
83 | 82 |
|
84 | 83 |
|
85 | 84 |
def network_to_dict(network, user_id, detail=True): |
86 |
network_id = str(network.id) if not network.public else 'public' |
|
87 |
d = {'id': network_id, 'name': network.name} |
|
85 |
d = {'id': str(network.id), 'name': network.name} |
|
88 | 86 |
if detail: |
89 | 87 |
d['cidr'] = network.subnet |
90 | 88 |
d['cidr6'] = network.subnet6 |
... | ... | |
95 | 93 |
d['updated'] = util.isoformat(network.updated) |
96 | 94 |
d['created'] = util.isoformat(network.created) |
97 | 95 |
d['status'] = network.state |
96 |
d['public'] = network.public |
|
98 | 97 |
|
99 |
attachments = [util.construct_nic_id(nic) for nic in network.nics.filter(machine__userid= user_id)] |
|
100 |
d['attachments'] = {'values':attachments} |
|
98 |
attachments = [util.construct_nic_id(nic) |
|
99 |
for nic in network.nics.filter(machine__userid=user_id)] |
|
100 |
d['attachments'] = {'values': attachments} |
|
101 | 101 |
return d |
102 | 102 |
|
103 | 103 |
|
b/snf-cyclades-app/synnefo/api/servers.py | ||
---|---|---|
108 | 108 |
else: |
109 | 109 |
return method_not_allowed(request) |
110 | 110 |
|
111 |
def nic_to_dict(nic): |
|
112 |
network = nic.network |
|
113 |
network_id = str(network.id) if not network.public else 'public' |
|
114 |
ipv4 = nic.ipv4 if nic.ipv4 else None |
|
115 |
ipv6 = nic.ipv6 if nic.ipv6 else None |
|
116 | 111 |
|
117 |
d = {'id': util.construct_nic_id(nic), 'network_id': network_id, 'mac_address': nic.mac, 'ipv4': ipv4, 'ipv6': ipv6} |
|
112 |
def nic_to_dict(nic): |
|
113 |
d = {'id': util.construct_nic_id(nic), |
|
114 |
'network_id': str(nic.network.id), |
|
115 |
'mac_address': nic.mac, |
|
116 |
'ipv4': nic.ipv4 if nic.ipv4 else None, |
|
117 |
'ipv6': nic.ipv6 if nic.ipv6 else None} |
|
118 | 118 |
if nic.firewall_profile: |
119 | 119 |
d['firewallProfile'] = nic.firewall_profile |
120 | 120 |
return d |
b/snf-cyclades-app/synnefo/api/util.py | ||
---|---|---|
56 | 56 |
|
57 | 57 |
from synnefo.api.faults import (Fault, BadRequest, BuildInProgress, |
58 | 58 |
ItemNotFound, ServiceUnavailable, Unauthorized, |
59 |
BadMediaType, OverLimit)
|
|
59 |
BadMediaType) |
|
60 | 60 |
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata, |
61 |
Network, NetworkInterface, BridgePool,
|
|
62 |
MacPrefixPool, Pool)
|
|
61 |
Network, BackendNetwork, NetworkInterface,
|
|
62 |
BridgePool)
|
|
63 | 63 |
|
64 | 64 |
from synnefo.lib.astakos import get_user |
65 | 65 |
from synnefo.plankton.backend import ImageBackend |
66 |
from synnefo.logic import ippool |
|
66 | 67 |
|
67 | 68 |
|
68 | 69 |
log = getLogger('synnefo.api') |
... | ... | |
202 | 203 |
"""Return a Network instance or raise ItemNotFound.""" |
203 | 204 |
|
204 | 205 |
try: |
205 |
if network_id == 'public': |
|
206 |
return Network.objects.get(public=True) |
|
207 |
else: |
|
208 |
network_id = int(network_id) |
|
209 |
return Network.objects.get(id=network_id, userid=user_id) |
|
206 |
network_id = int(network_id) |
|
207 |
return Network.objects.get(id=network_id, userid=user_id) |
|
210 | 208 |
except (ValueError, Network.DoesNotExist): |
211 | 209 |
raise ItemNotFound('Network not found.') |
212 | 210 |
|
213 | 211 |
|
212 |
def backend_public_networks(backend): |
|
213 |
"""Return available public networks of the backend. |
|
214 |
|
|
215 |
Iterator for non-deleted public networks that are available |
|
216 |
to the specified backend. |
|
217 |
|
|
218 |
""" |
|
219 |
for network in Network.objects.filter(public=True, deleted=False): |
|
220 |
if BackendNetwork.objects.filter(network=network, |
|
221 |
backend=backend).exists(): |
|
222 |
yield network |
|
223 |
|
|
224 |
|
|
225 |
def get_network_free_address(network): |
|
226 |
"""Reserve an IP address from the IP Pool of the network. |
|
227 |
|
|
228 |
Raises Network.DoesNotExist , ippool.IPPool.IPPoolExhausted |
|
229 |
|
|
230 |
""" |
|
231 |
|
|
232 |
# Get the Network object in exclusive mode in order to |
|
233 |
# safely (isolated) reserve an IP address |
|
234 |
network = Network.objects.select_for_update().get(id=network.id) |
|
235 |
pool = ippool.IPPool(network) |
|
236 |
address = pool.get_free_address() |
|
237 |
pool.save() |
|
238 |
return address |
|
239 |
|
|
240 |
|
|
214 | 241 |
def get_nic(machine, network): |
215 | 242 |
try: |
216 | 243 |
return NetworkInterface.objects.get(machine=machine, network=network) |
217 | 244 |
except NetworkInterface.DoesNotExist: |
218 | 245 |
raise ItemNotFound('Server not connected to this network.') |
219 | 246 |
|
247 |
|
|
220 | 248 |
def get_nic_from_index(vm, nic_index): |
221 | 249 |
"""Returns the nic_index-th nic of a vm |
222 | 250 |
Error Response Codes: itemNotFound (404), badMediaType (415) |
... | ... | |
230 | 258 |
nic = matching_nics[0] |
231 | 259 |
return nic |
232 | 260 |
|
261 |
|
|
233 | 262 |
def get_request_dict(request): |
234 | 263 |
"""Returns data sent by the client as a python dict.""" |
235 | 264 |
|
... | ... | |
336 | 365 |
raise Unauthorized('No user found.') |
337 | 366 |
if http_method and request.method != http_method: |
338 | 367 |
raise BadRequest('Method not allowed.') |
339 |
|
|
368 |
|
|
340 | 369 |
resp = func(request, *args, **kwargs) |
341 | 370 |
update_response_headers(request, resp) |
342 | 371 |
return resp |
... | ... | |
348 | 377 |
return render_fault(request, fault) |
349 | 378 |
except Fault, fault: |
350 | 379 |
return render_fault(request, fault) |
351 |
except BaseException, e:
|
|
380 |
except BaseException: |
|
352 | 381 |
log.exception('Unexpected error') |
353 | 382 |
fault = ServiceUnavailable('Unexpected error.') |
354 | 383 |
return render_fault(request, fault) |
b/snf-cyclades-app/synnefo/app_settings/default/api.py | ||
---|---|---|
23 | 23 |
|
24 | 24 |
# Maximum allowed network size for private networks. |
25 | 25 |
MAX_CIDR_BLOCK = 22 |
26 |
# Name of the network in Ganeti corresponding to the default public network. |
|
27 |
# All created VMs will obtain an IP from this network. |
|
28 |
GANETI_PUBLIC_NETWORK = 'snf-net-1' |
|
29 | 26 |
|
30 | 27 |
# The first mac prefix to use |
31 | 28 |
MAC_POOL_BASE = 'aa:00:0' |
b/snf-cyclades-app/synnefo/logic/allocators/default_allocator.py | ||
---|---|---|
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 | 34 |
from __future__ import division |
35 |
from synnefo.api.util import backend_public_networks |
|
35 | 36 |
|
36 | 37 |
|
37 | 38 |
def allocate(backends, vm): |
38 | 39 |
if len(backends) == 1: |
39 |
return backends.keys()[0]
|
|
40 |
return backends[0] |
|
40 | 41 |
|
41 | 42 |
# Filter those that can not host the vm |
42 |
capable_backends = dict((k, v) for k, v in backends.iteritems()\
|
|
43 |
if vm_fits_in_backend(v, vm))
|
|
43 |
capable_backends = [backend for backend in backends
|
|
44 |
if vm_fits_in_backend(backend, vm)]
|
|
44 | 45 |
|
45 | 46 |
# Since we are conservatively updating backend resources on each |
46 | 47 |
# allocation, a backend may actually be able to host a vm (despite |
... | ... | |
49 | 50 |
capable_backends = backends |
50 | 51 |
|
51 | 52 |
# Compute the scores for each backend |
52 |
backend_scores = [(back_id, backend_score(back_resources, vm))\ |
|
53 |
for back_id, back_resources in \ |
|
54 |
capable_backends.iteritems()] |
|
53 |
backend_scores = [(backend, backend_score(backend, vm)) |
|
54 |
for backend in capable_backends] |
|
55 | 55 |
|
56 | 56 |
# Pick out the best |
57 |
result = min(backend_scores, key=lambda (b_id, b_score): b_score)
|
|
58 |
backend_id = result[0]
|
|
57 |
result = min(backend_scores, key=lambda (b, b_score): b_score) |
|
58 |
backend = result[0] |
|
59 | 59 |
|
60 |
return backend_id
|
|
60 |
return backend |
|
61 | 61 |
|
62 | 62 |
|
63 | 63 |
def vm_fits_in_backend(backend, vm): |
64 |
return backend['dfree'] > vm['disk'] and backend['mfree'] > vm['ram'] |
|
64 |
return backend.dfree > vm['disk'] and backend.mfree > vm['ram'] and\ |
|
65 |
has_free_ip(backend) |
|
65 | 66 |
|
66 | 67 |
|
67 | 68 |
def backend_score(backend, flavor): |
68 |
mratio = 1 - (backend['mfree'] / backend['mtotal'])
|
|
69 |
dratio = 1 - (backend['dfree'] / backend['dtotal'])
|
|
70 |
cratio = (backend['pinst_cnt'] + 1) / (backend['ctotal'] * 4)
|
|
69 |
mratio = 1 - (backend.mfree / backend.mtotal)
|
|
70 |
dratio = 1 - (backend.dfree / backend.dtotal)
|
|
71 |
cratio = (backend.pinst_cnt + 1) / (backend.ctotal * 4)
|
|
71 | 72 |
return 0.7 * (mratio + dratio) * 0.3 * cratio |
73 |
|
|
74 |
|
|
75 |
def has_free_ip(backend): |
|
76 |
"""Find if Backend has any free public IP.""" |
|
77 |
for network in backend_public_networks(backend): |
|
78 |
if not network.pool.is_full(): |
|
79 |
return True |
|
80 |
return False |
b/snf-cyclades-app/synnefo/logic/backend.py | ||
---|---|---|
42 | 42 |
BackendNetwork, BACKEND_STATUSES) |
43 | 43 |
from synnefo.logic import utils, ippool |
44 | 44 |
from synnefo.api.faults import OverLimit |
45 |
from synnefo.api.util import backend_public_networks, get_network_free_address |
|
45 | 46 |
from synnefo.util.rapi import GanetiRapiClient |
46 | 47 |
|
47 | 48 |
log = getLogger('synnefo.logic') |
... | ... | |
285 | 286 |
""" |
286 | 287 |
|
287 | 288 |
if settings.PUBLIC_ROUTED_USE_POOL: |
288 |
# Get the Network object in exclusive mode in order to |
|
289 |
# safely (isolated) reserve an IP address |
|
290 |
try: |
|
291 |
network = Network.objects.select_for_update().get(public=True) |
|
292 |
except Network.DoesNotExist: |
|
293 |
raise Exception('No public network available') |
|
294 |
pool = ippool.IPPool(network) |
|
295 |
try: |
|
296 |
address = pool.get_free_address() |
|
297 |
except ippool.IPPool.IPPoolExhausted: |
|
289 |
(network, address) = allocate_public_address(vm) |
|
290 |
if address is None: |
|
298 | 291 |
raise OverLimit("Can not allocate IP for new machine." |
299 |
" Public network is full.") |
|
300 |
pool.save() |
|
301 |
nic = {'ip': address, 'network': settings.GANETI_PUBLIC_NETWORK} |
|
292 |
" Public networks are full.") |
|
293 |
nic = {'ip': address, 'network': network.backend_id} |
|
302 | 294 |
else: |
303 |
nic = {'ip': 'pool', 'network': settings.GANETI_PUBLIC_NETWORK}
|
|
295 |
nic = {'ip': 'pool', 'network': network.backend_id}
|
|
304 | 296 |
|
305 | 297 |
if settings.IGNORE_FLAVOR_DISK_SIZES: |
306 | 298 |
if image['backend_id'].find("windows") >= 0: |
... | ... | |
335 | 327 |
if provider: |
336 | 328 |
kw['disks'][0]['provider'] = provider |
337 | 329 |
|
338 |
|
|
339 | 330 |
kw['nics'] = [nic] |
340 | 331 |
# Defined in settings.GANETI_CREATEINSTANCE_KWARGS |
341 | 332 |
# kw['os'] = settings.GANETI_OS_PROVIDER |
... | ... | |
367 | 358 |
return vm.client.CreateInstance(**kw) |
368 | 359 |
|
369 | 360 |
|
361 |
def allocate_public_address(vm): |
|
362 |
"""Allocate a public IP for a vm.""" |
|
363 |
for network in backend_public_networks(vm.backend): |
|
364 |
try: |
|
365 |
address = get_network_free_address(network) |
|
366 |
return (network, address) |
|
367 |
except ippool.IPPool.IPPoolExhausted: |
|
368 |
pass |
|
369 |
return (None, None) |
|
370 |
|
|
371 |
|
|
370 | 372 |
def delete_instance(vm): |
371 | 373 |
start_action(vm, 'DESTROY') |
372 | 374 |
vm.client.DeleteInstance(vm.backend_vm_id, dry_run=settings.TEST) |
b/snf-cyclades-app/synnefo/logic/backend_allocator.py | ||
---|---|---|
61 | 61 |
|
62 | 62 |
# Find the best backend to host the vm, based on the allocation |
63 | 63 |
# strategy |
64 |
backend_id = self.strategy_mod.allocate(available_backends, vm)
|
|
64 |
backend = self.strategy_mod.allocate(available_backends, vm) |
|
65 | 65 |
|
66 |
backend = Backend.objects.get(id=backend_id) |
|
67 | 66 |
# Reduce the free resources of the selected backend by the size of |
68 | 67 |
# the vm |
69 | 68 |
reduce_backend_resources(backend, vm) |
... | ... | |
75 | 74 |
"""Get available backends from db. |
76 | 75 |
|
77 | 76 |
""" |
78 |
attrs = ['mfree', 'mtotal', 'dfree', 'dtotal', 'pinst_cnt', 'ctotal'] |
|
79 |
backends = {} |
|
80 |
for b in Backend.objects.filter(drained=False, offline=False): |
|
81 |
backend = {} |
|
82 |
for a in attrs: |
|
83 |
backend[a] = getattr(b, a) |
|
84 |
backends[b.id] = backend |
|
85 |
return backends |
|
77 |
return Backend.objects.filter(drained=False, offline=False) |
|
86 | 78 |
|
87 | 79 |
|
88 | 80 |
def flavor_disk(flavor): |
b/snf-cyclades-app/synnefo/logic/management/commands/backend-add.py | ||
---|---|---|
133 | 133 |
self.stdout.write(sep + '\n\n') |
134 | 134 |
|
135 | 135 |
for net in networks: |
136 |
if net.public: |
|
137 |
# Do not create public networks since are backend-specific |
|
138 |
continue |
|
136 | 139 |
net.create_backend_network(backend) |
137 | 140 |
result = create_network_synced(net, backend) |
138 | 141 |
if result[0] != "success": |
b/snf-cyclades-app/synnefo/logic/management/commands/reconcile-networks.py | ||
---|---|---|
92 | 92 |
|
93 | 93 |
# Perform reconcilliation for each backend |
94 | 94 |
for b in backends: |
95 |
if network.public and not \ |
|
96 |
BackendNetwork.objects.filter(network=network, |
|
97 |
backend=b).exists(): |
|
98 |
continue |
|
99 |
|
|
95 | 100 |
info = (net_id, b.clustername) |
96 | 101 |
back_network = None |
97 | 102 |
|
Also available in: Unified diff