Revision 68b952f9

b/snf-cyclades-app/synnefo/db/aes_encrypt.py
29 29

  
30 30

  
31 31
def encrypt_db_charfield(plaintext):
32
    if plaintext == None:
32
    if not plaintext:
33 33
        return plaintext
34 34
    salt = "".join([choice(letters + digits) for i in xrange(SALT_LEN)])
35 35

  
......
42 42

  
43 43

  
44 44
def decrypt_db_charfield(ciphertext):
45
    if ciphertext == None:
45
    if not ciphertext:
46 46
        return ciphertext
47 47
    has_prefix = ciphertext.startswith(DB_ENCRYPTED_FIELD_PREFIX + ':')
48 48
    if not has_prefix:  # Non-encoded value
b/snf-cyclades-app/synnefo/db/managers.py
71 71
        if num == 1:
72 72
            return query[0]
73 73
        if not num:
74
            raise self.model.DoesNotExist(
75
                    "%s matching query does not exist. "
76
                    "Lookup parameters were %s" %
77
                    (self.model._meta.object_name, kwargs))
74
            raise self.model.DoesNotExist("%s matching query does not exist. "
75
                                          "Lookup parameters were %s" %
76
                                          (self.model._meta.object_name,
77
                                           kwargs))
78 78
        raise self.model.MultipleObjectsReturned(
79 79
            "get() returned more than one %s -- it returned %s! "
80 80
            "Lookup parameters were %s" %
b/snf-cyclades-app/synnefo/db/models.py
55 55
    ram = models.IntegerField('RAM size in MiB', default=0)
56 56
    disk = models.IntegerField('Disk size in GiB', default=0)
57 57
    disk_template = models.CharField('Disk template', max_length=32,
58
            default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
58
                       default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
59 59
    deleted = models.BooleanField('Deleted', default=False)
60 60

  
61 61
    class Meta:
......
78 78
    username = models.CharField('Username', max_length=64, blank=True,
79 79
                                null=True)
80 80
    password_hash = models.CharField('Password', max_length=128, blank=True,
81
                                null=True)
81
                                     null=True)
82 82
    # Sha1 is up to 40 characters long
83 83
    hash = models.CharField('Hash', max_length=40, editable=False, null=False)
84 84
    # Unique index of the Backend, used for the mac-prefixes of the
......
128 128

  
129 129
    def create_hash(self):
130 130
        """Create a hash for this backend. """
131
        return sha1('%s%s%s%s' % \
132
                (self.clustername, self.port, self.username, self.password)) \
133
                .hexdigest()
131
        sha = sha1('%s%s%s%s' %
132
                   (self.clustername, self.port, self.username, self.password))
133
        return sha.hexdigest()
134 134

  
135 135
    @property
136 136
    def password(self):
......
147 147
        super(Backend, self).save(*args, **kwargs)
148 148
        if self.hash != old_hash:
149 149
            # Populate the new hash to the new instances
150
            self.virtual_machines.filter(deleted=False).update(backend_hash=self.hash)
150
            self.virtual_machines.filter(deleted=False)\
151
                                 .update(backend_hash=self.hash)
151 152

  
152 153
    def delete(self, *args, **kwargs):
153 154
        # Integrity Error if non-deleted VMs are associated with Backend
......
192 193

  
193 194

  
194 195
class QuotaHolderSerial(models.Model):
195
    serial = models.BigIntegerField(null=False, primary_key=True, db_index=True)
196
    serial = models.BigIntegerField(null=False, primary_key=True,
197
                                    db_index=True)
196 198
    pending = models.BooleanField(default=True, db_index=True)
197 199
    accepted = models.BooleanField(default=False)
198 200
    rejected = models.BooleanField(default=False)
......
209 211
class VirtualMachine(models.Model):
210 212
    # The list of possible actions for a VM
211 213
    ACTIONS = (
212
       ('CREATE', 'Create VM'),
213
       ('START', 'Start VM'),
214
       ('STOP', 'Shutdown VM'),
215
       ('SUSPEND', 'Admin Suspend VM'),
216
       ('REBOOT', 'Reboot VM'),
217
       ('DESTROY', 'Destroy VM')
214
        ('CREATE', 'Create VM'),
215
        ('START', 'Start VM'),
216
        ('STOP', 'Shutdown VM'),
217
        ('SUSPEND', 'Admin Suspend VM'),
218
        ('REBOOT', 'Reboot VM'),
219
        ('DESTROY', 'Destroy VM')
218 220
    )
219 221

  
220 222
    # The internal operating state of a VM
......
296 298
    suspended = models.BooleanField('Administratively Suspended',
297 299
                                    default=False)
298 300
    serial = models.ForeignKey(QuotaHolderSerial,
299
                              related_name='virtual_machine', null=True)
301
                               related_name='virtual_machine', null=True)
300 302

  
301 303
    # VM State
302 304
    # The following fields are volatile data, in the sense
......
422 424
    )
423 425

  
424 426
    ACTIONS = (
425
       ('CREATE', 'Create Network'),
426
       ('DESTROY', 'Destroy Network'),
427
        ('CREATE', 'Create Network'),
428
        ('DESTROY', 'Destroy Network'),
427 429
    )
428 430

  
429 431
    RSAPI_STATE_FROM_OPER_STATE = {
......
435 437

  
436 438
    FLAVORS = {
437 439
        'CUSTOM': {
438
             'mode': 'bridged',
439
             'link': settings.DEFAULT_BRIDGE,
440
             'mac_prefix': settings.DEFAULT_MAC_PREFIX,
441
             'tags': None,
442
             'desc': "Basic flavor used for a bridged network",
440
            'mode': 'bridged',
441
            'link': settings.DEFAULT_BRIDGE,
442
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
443
            'tags': None,
444
            'desc': "Basic flavor used for a bridged network",
443 445
        },
444 446
        'IP_LESS_ROUTED': {
445
             'mode': 'routed',
446
             'link': settings.DEFAULT_ROUTING_TABLE,
447
             'mac_prefix': settings.DEFAULT_MAC_PREFIX,
448
             'tags': 'ip-less-routed',
449
             'desc': "Flavor used for an IP-less routed network using"
450
                     " Proxy ARP",
447
            'mode': 'routed',
448
            'link': settings.DEFAULT_ROUTING_TABLE,
449
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
450
            'tags': 'ip-less-routed',
451
            'desc': "Flavor used for an IP-less routed network using"
452
                    " Proxy ARP",
451 453
        },
452 454
        'MAC_FILTERED': {
453
             'mode': 'bridged',
454
             'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
455
             'mac_prefix': 'pool',
456
             'tags': 'private-filtered',
457
             'desc': "Flavor used for bridged networks that offer isolation"
458
                     " via filtering packets based on their src "
459
                     " MAC (ebtables)",
455
            'mode': 'bridged',
456
            'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
457
            'mac_prefix': 'pool',
458
            'tags': 'private-filtered',
459
            'desc': "Flavor used for bridged networks that offer isolation"
460
                    " via filtering packets based on their src "
461
                    " MAC (ebtables)",
460 462
        },
461 463
        'PHYSICAL_VLAN': {
462
             'mode': 'bridged',
463
             'link': 'pool',
464
             'mac_prefix': settings.DEFAULT_MAC_PREFIX,
465
             'tags': 'physical-vlan',
466
             'desc': "Flavor used for bridged network that offer isolation"
467
                     " via dedicated physical vlan",
464
            'mode': 'bridged',
465
            'link': 'pool',
466
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
467
            'tags': 'physical-vlan',
468
            'desc': "Flavor used for bridged network that offer isolation"
469
                    " via dedicated physical vlan",
468 470
        },
469 471
    }
470 472

  
......
528 530
        backends = [backend] if backend\
529 531
                             else Backend.objects.filter(offline=False)
530 532
        for backend in backends:
531
            if not BackendNetwork.objects.filter(backend=backend, network=self)\
532
                                 .exists():
533
            backend_exists =\
534
                BackendNetwork.objects.filter(backend=backend, network=self)\
535
                                      .exists()
536
            if not backend_exists:
533 537
                BackendNetwork.objects.create(backend=backend, network=self)
534 538

  
535 539
    def get_pool(self):
......
564 568
            self.status = status
565 569

  
566 570
        def __str__(self):
567
            return repr('<opcode: %s, status: %s>' % (self.opcode,
568
                    self.status))
571
            return repr('<opcode: %s, status: %s>'
572
                        % (self.opcode, self.status))
569 573

  
570 574
    class InvalidActionError(Exception):
571 575
        def __init__(self, action):
......
647 651
            try:
648 652
                utils.validate_mac(mac_prefix + ":00:00:00")
649 653
            except utils.InvalidMacAddress:
650
                raise utils.InvalidMacAddress("Invalid MAC prefix '%s'" % \
651
                                               mac_prefix)
654
                raise utils.InvalidMacAddress("Invalid MAC prefix '%s'" %
655
                                              mac_prefix)
652 656
            self.mac_prefix = mac_prefix
653 657

  
654 658

  
......
754 758

  
755 759
    def since(self, vm, created_since, **kwargs):
756 760
        return self.get_query_set().filter(vm=vm, created__gt=created_since,
757
                **kwargs)
761
                                           **kwargs)
758 762

  
759 763

  
760 764
class VirtualMachineDiagnostic(models.Model):
b/snf-cyclades-app/synnefo/db/models_factory.py
173 173
    network = factory.SubFactory(NetworkFactory)
174 174
    index = factory.Sequence(lambda x: x, type=int)
175 175
    mac = factory.Sequence(lambda n:
176
            'aa:{0}{0}:{0}{0}:aa:{0}{0}:{0}{0}'.format(hex(int(n) % 15)[2:3]))
177
    ipv4 = factory.LazyAttributeSequence(lambda a, n: a.network.subnet[:-4] + \
176
        'aa:{0}{0}:{0}{0}:aa:{0}{0}:{0}{0}'.format(hex(int(n) % 15)[2:3]))
177
    ipv4 = factory.LazyAttributeSequence(lambda a, n: a.network.subnet[:-4] +
178 178
                                         '{0}'.format(int(n) + 2))
179 179
    firewall_profile =\
180 180
        factory.Sequence(round_seq_first(FACTORY_FOR.FIREWALL_PROFILES))
b/snf-cyclades-app/synnefo/logic/utils.py
33 33
from django.conf import settings
34 34
from copy import deepcopy
35 35

  
36

  
36 37
def id_from_instance_name(name):
37 38
    """Returns VirtualMachine's Django id, given a ganeti machine name.
38 39

  
......
82 83
      (vm.operstate) through the RSAPI_STATE_FROM_OPER_STATE dictionary.
83 84

  
84 85
      The last state reported by Ganeti is set whenever Ganeti reports
85
      successful completion of an operation. If Ganeti says an OP_INSTANCE_STARTUP
86
      operation succeeded, vm.operstate is set to "STARTED".
87

  
88
    * To support any transitional states defined by the API (only REBOOT for the time
89
      being) this mapping is amended with information reported by Ganeti regarding
90
      any outstanding operation. If an OP_INSTANCE_STARTUP had succeeded previously
91
      and an OP_INSTANCE_REBOOT has been reported as in progress, the API state is
92
      "REBOOT".
86
      successful completion of an operation. If Ganeti says an
87
      OP_INSTANCE_STARTUP operation succeeded, vm.operstate is set to
88
      "STARTED".
89

  
90
    * To support any transitional states defined by the API (only REBOOT for
91
    the time being) this mapping is amended with information reported by Ganeti
92
    regarding any outstanding operation. If an OP_INSTANCE_STARTUP had
93
    succeeded previously and an OP_INSTANCE_REBOOT has been reported as in
94
    progress, the API state is "REBOOT".
93 95

  
94 96
    """
95 97
    try:
b/snf-cyclades-app/synnefo/quotas/__init__.py
77 77
            if resource == "cyclades.network.private":
78 78
                user_networks = Network.objects.filter(userid=userid,
79 79
                                                       deleted=False).count()
80
                user_network_limit = NETWORKS_USER_QUOTA.get(userid,
81
                                                         MAX_NETWORKS_PER_USER)
80
                user_network_limit =\
81
                    NETWORKS_USER_QUOTA.get(userid, MAX_NETWORKS_PER_USER)
82 82
                if user_networks + size >= user_network_limit:
83 83
                    raise NoQuantityError()
84 84

  
......
244 244
        resources = invert_resources(resources)
245 245
    provisions = [('cyclades', 'cyclades.' + r, s)
246 246
                  for r, s in resources.items()]
247
    return  {"context":    {},
248
             "target":     user,
249
             "key":        "1",
250
             "clientkey":  "cyclades",
251
             #"owner":      "",
252
             #"ownerkey":   "1",
253
             "name":       "",
254
             "provisions": provisions}
247
    return {"context": {},
248
            "target": user,
249
            "key": "1",
250
            "clientkey": "cyclades",
251
            #"owner":      "",
252
            #"ownerkey":   "1",
253
            "name": "",
254
            "provisions": provisions}
255 255

  
256 256
##
257 257
## Reconcile pending commissions
b/snf-cyclades-app/synnefo/quotas/management/commands/quotas-init.py
69 69
                    continue
70 70
                reset_holding = []
71 71
                for res, val in resources.items():
72
                    reset_holding.append((user, "cyclades." + res, "1", val, 0, 0, 0))
72
                    reset_holding.append((user, "cyclades." + res, "1", val, 0,
73
                                          0, 0))
73 74
                if not options['dry_run']:
74 75
                    try:
75
                        qh.reset_holding(context={}, reset_holding=reset_holding)
76
                        qh.reset_holding(context={},
77
                                         reset_holding=reset_holding)
76 78
                    except Exception as e:
77 79
                        self.stderr.write("Can not set up holding:%s" % e)
78 80
                else:
b/snf-cyclades-app/synnefo/quotas/management/commands/quotas-verify.py
86 86
        db_extra = db_res - qh_res
87 87
        if db_extra:
88 88
            for res in db_extra:
89
                write("Resource %s exists in DB for %s but not in QH\n"\
89
                write("Resource %s exists in DB for %s but not in QH\n"
90 90
                      % (res, user))
91 91
        qh_extra = qh_res - db_res
92 92
        if qh_extra:
93 93
            for res in qh_extra:
94
                write("Resource %s exists in QH for %s but not in DB\n"\
94
                write("Resource %s exists in QH for %s but not in DB\n"
95 95
                      % (res, user))
96 96
        return False
b/snf-cyclades-app/synnefo/quotas/management/commands/reconcile-quotas.py
54 54
        accepted, rejected = quotas.resolve_pending_commissions()
55 55

  
56 56
        if accepted:
57
            self.stdout.write("Pending accepted commissions:\n %s\n" \
57
            self.stdout.write("Pending accepted commissions:\n %s\n"
58 58
                              % list_to_string(accepted))
59 59

  
60 60
        if rejected:
61
            self.stdout.write("Pending rejected commissions:\n %s\n" \
61
            self.stdout.write("Pending rejected commissions:\n %s\n"
62 62
                              % list_to_string(rejected))
63 63

  
64 64
        if fix and (accepted or rejected):
b/snf-cyclades-app/synnefo/quotas/util.py
65 65

  
66 66
    # Get resources related with networks
67 67
    net_resources = networks.values("userid")\
68
                                    .annotate(num=Count("id"))
68
                            .annotate(num=Count("id"))
69 69
    for net_res in net_resources:
70 70
        user = net_res['userid']
71 71
        if user not in holdings:
......
99 99

  
100 100

  
101 101
def decode_holding(holding):
102
    entity, resource, imported, exported, returned, released = \
103
            holding
102
    entity, resource, imported, exported, returned, released = holding
104 103
    res = resource.replace("cyclades.", "")
105 104
    return (res, imported - exported + returned - released)
b/snf-cyclades-app/synnefo/vmapi/__init__.py
38 38

  
39 39
from synnefo.vmapi.settings import CACHE_KEY_PREFIX, CACHE_BACKEND
40 40

  
41

  
41 42
def get_uuid():
42 43
    return str(uuid4())
43 44

  
45

  
44 46
def get_key(*args):
45 47
    args = map(str, filter(bool, list(args)))
46 48
    args.insert(0, CACHE_KEY_PREFIX)
......
54 56
# here.
55 57
if hasattr(backend, 'close'):
56 58
    signals.request_finished.connect(backend.close)
57

  
b/snf-cyclades-app/synnefo/vmapi/models.py
41 41

  
42 42
log = getLogger('synnefo.vmapi')
43 43

  
44

  
44 45
def create_server_params(sender, created_vm_params, **kwargs):
45 46
    json_value = json.dumps(created_vm_params)
46 47
    uuid = get_uuid()
......
55 56
    return uuid
56 57

  
57 58
server_created.connect(create_server_params)
58

  
b/snf-cyclades-app/synnefo/vmapi/settings.py
34 34
from django.conf import settings
35 35

  
36 36
CACHE_BACKEND = getattr(settings, 'VMAPI_CACHE_BACKEND',
37
    settings.CACHE_BACKEND)
37
                        settings.CACHE_BACKEND)
38 38
CACHE_KEY_PREFIX = getattr(settings, 'VMAPI_CACHE_KEY_PREFIX',
39
    'vmapi')
39
                           'vmapi')
40 40
RESET_PARAMS = getattr(settings, 'VMAPI_RESET_PARAMS', True)
41 41
BASE_URL = getattr(settings, 'VMAPI_BASE_URL',
42
                       'https://cyclades.okeanos.grnet.gr/')
42
                   'https://cyclades.okeanos.grnet.gr/')
b/snf-cyclades-app/synnefo/vmapi/tests.py
37 37

  
38 38
from synnefo.vmapi import settings
39 39

  
40

  
40 41
class TestServerParams(TestCase):
41 42

  
42 43
    def test_cache_backend(self):
......
55 56
        from synnefo.vmapi.models import create_server_params
56 57
        from synnefo.vmapi import backend
57 58
        try:
58
            from synnefo.api.servers import server_created
59 59
            from synnefo.db.models import VirtualMachine
60 60
        except ImportError:
61 61
            print "Skipping test_params_create"
......
66 66
        params = {'password': 'X^942Jjfdsa', 'personality': {}}
67 67
        uuid = create_server_params(sender=vm, created_vm_params=params)
68 68

  
69
        self.assertEqual(vm.config_url, settings.BASE_URL + '/vmapi/server-params/%s' % uuid)
69
        self.assertEqual(vm.config_url, settings.BASE_URL +
70
                         '/vmapi/server-params/%s' % uuid)
70 71
        key = "vmapi_%s" % uuid
71 72
        self.assertEqual(type(backend.get(key)), str)
72 73
        data = json.loads(backend.get(key))
......
78 79
        self.assertEqual(response.status_code, 200)
79 80
        response = self.client.get('/vmapi/server-params/%s' % uuid)
80 81
        self.assertEqual(response.status_code, 404)
81

  
82

  
83
    def test_params_view(self):
84
        pass
85

  
b/snf-cyclades-app/synnefo/vmapi/urls.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from django.conf.urls.defaults import include, patterns, url
34
from django.conf.urls.defaults import patterns, url
35 35

  
36 36
urlpatterns = patterns('synnefo.vmapi.views',
37
    url(r'^server-params/(?P<uuid>.*)$', 'server_params', name="vmapi_server_params"),
38
)
37
                       url(r'^server-params/(?P<uuid>.*)$', 'server_params',
38
                           name="vmapi_server_params"),)
b/snf-cyclades-app/synnefo/vmapi/views.py
40 40

  
41 41
log = getLogger('synnefo.vmapi')
42 42

  
43

  
43 44
def server_params(request, uuid):
44 45
    if not uuid:
45 46
        raise Http404
......
54 55
        backend.set(cache_key, None)
55 56

  
56 57
    return HttpResponse(params, content_type="application/json")
57

  

Also available in: Unified diff