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