Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / db / models.py @ d30f29aa

History | View | Annotate | Download (23.4 kB)

1 244c552b Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 48130e66 Georgios Gousios
#
3 48130e66 Georgios Gousios
# Redistribution and use in source and binary forms, with or without
4 48130e66 Georgios Gousios
# modification, are permitted provided that the following conditions
5 48130e66 Georgios Gousios
# are met:
6 48130e66 Georgios Gousios
#
7 48130e66 Georgios Gousios
#   1. Redistributions of source code must retain the above copyright
8 48130e66 Georgios Gousios
#      notice, this list of conditions and the following disclaimer.
9 48130e66 Georgios Gousios
#
10 48130e66 Georgios Gousios
#  2. Redistributions in binary form must reproduce the above copyright
11 48130e66 Georgios Gousios
#     notice, this list of conditions and the following disclaimer in the
12 48130e66 Georgios Gousios
#     documentation and/or other materials provided with the distribution.
13 48130e66 Georgios Gousios
#
14 48130e66 Georgios Gousios
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 48130e66 Georgios Gousios
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 48130e66 Georgios Gousios
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 48130e66 Georgios Gousios
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 48130e66 Georgios Gousios
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 48130e66 Georgios Gousios
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 48130e66 Georgios Gousios
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 48130e66 Georgios Gousios
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 48130e66 Georgios Gousios
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 48130e66 Georgios Gousios
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 48130e66 Georgios Gousios
# SUCH DAMAGE.
25 48130e66 Georgios Gousios
#
26 48130e66 Georgios Gousios
# The views and conclusions contained in the software and documentation are
27 48130e66 Georgios Gousios
# those of the authors and should not be interpreted as representing official
28 48130e66 Georgios Gousios
# policies, either expressed or implied, of GRNET S.A.
29 48130e66 Georgios Gousios
30 c4e55622 Christos Stavrakakis
import datetime
31 c4e55622 Christos Stavrakakis
32 9dbe70f8 Markos Gogoulos
from django.conf import settings
33 17935f41 Vassilios Karakoidas
from django.db import models
34 aed9b901 Christos Stavrakakis
from django.db import IntegrityError
35 aed9b901 Christos Stavrakakis
36 aed9b901 Christos Stavrakakis
from hashlib import sha1
37 aed9b901 Christos Stavrakakis
from synnefo.api.faults import ServiceUnavailable
38 aed9b901 Christos Stavrakakis
from synnefo.util.rapi import GanetiRapiClient
39 e6a42a96 Christos Stavrakakis
from synnefo import settings as snf_settings
40 aed9b901 Christos Stavrakakis
41 aed9b901 Christos Stavrakakis
42 aed9b901 Christos Stavrakakis
BACKEND_CLIENTS = {}    #{hash:Backend client}
43 aed9b901 Christos Stavrakakis
BACKEND_HASHES = {}     #{Backend.id:hash}
44 aed9b901 Christos Stavrakakis
45 aed9b901 Christos Stavrakakis
def get_client(hash, backend):
46 aed9b901 Christos Stavrakakis
    """Get a cached backend client or create a new one.
47 aed9b901 Christos Stavrakakis

48 aed9b901 Christos Stavrakakis
    @param hash: The hash of the backend
49 aed9b901 Christos Stavrakakis
    @param backend: Either a backend object or backend ID
50 aed9b901 Christos Stavrakakis
    """
51 aed9b901 Christos Stavrakakis
52 aed9b901 Christos Stavrakakis
    if backend is None:
53 aed9b901 Christos Stavrakakis
        raise Exception("Backend is None. Cannot create a client.")
54 aed9b901 Christos Stavrakakis
55 aed9b901 Christos Stavrakakis
    if hash in BACKEND_CLIENTS:
56 aed9b901 Christos Stavrakakis
        # Return cached client
57 aed9b901 Christos Stavrakakis
        return BACKEND_CLIENTS[hash]
58 aed9b901 Christos Stavrakakis
59 aed9b901 Christos Stavrakakis
    # Always get a new instance to ensure latest credentials
60 aed9b901 Christos Stavrakakis
    if isinstance(backend, Backend):
61 aed9b901 Christos Stavrakakis
        backend = backend.id
62 aed9b901 Christos Stavrakakis
    (credentials,) = Backend.objects.filter(id=backend).values_list('hash',
63 aed9b901 Christos Stavrakakis
                                'clustername', 'port', 'username', 'password')
64 aed9b901 Christos Stavrakakis
65 aed9b901 Christos Stavrakakis
    hash, clustername, port, user, password = credentials
66 aed9b901 Christos Stavrakakis
67 aed9b901 Christos Stavrakakis
    # Check client for updated hash
68 aed9b901 Christos Stavrakakis
    if hash in BACKEND_CLIENTS:
69 aed9b901 Christos Stavrakakis
        return BACKEND_CLIENTS[hash]
70 aed9b901 Christos Stavrakakis
71 aed9b901 Christos Stavrakakis
    # Delete old version of the client
72 aed9b901 Christos Stavrakakis
    if backend in BACKEND_HASHES:
73 aed9b901 Christos Stavrakakis
        del BACKEND_CLIENTS[BACKEND_HASHES[backend]]
74 aed9b901 Christos Stavrakakis
75 aed9b901 Christos Stavrakakis
    # Create the new client
76 aed9b901 Christos Stavrakakis
    client = GanetiRapiClient(clustername, port, user, password)
77 aed9b901 Christos Stavrakakis
78 aed9b901 Christos Stavrakakis
    # Store the client and the hash
79 aed9b901 Christos Stavrakakis
    BACKEND_CLIENTS[hash] = client
80 aed9b901 Christos Stavrakakis
    BACKEND_HASHES[backend] = hash
81 aed9b901 Christos Stavrakakis
82 aed9b901 Christos Stavrakakis
    return client
83 aed9b901 Christos Stavrakakis
84 aed9b901 Christos Stavrakakis
85 aed9b901 Christos Stavrakakis
def clear_client_cache():
86 aed9b901 Christos Stavrakakis
    BACKEND_CLIENTS.clear()
87 aed9b901 Christos Stavrakakis
    BACKEND_HASHES.clear()
88 7b7f9205 Vassilios Karakoidas
89 f5ad2956 Markos Gogoulos
90 e7164ebc Vassilios Karakoidas
class Flavor(models.Model):
91 13b3c5ff Vassilios Karakoidas
    cpu = models.IntegerField('Number of CPUs', default=0)
92 c909cbbd Giorgos Verigakis
    ram = models.IntegerField('RAM size in MiB', default=0)
93 c909cbbd Giorgos Verigakis
    disk = models.IntegerField('Disk size in GiB', default=0)
94 c909cbbd Giorgos Verigakis
    disk_template = models.CharField('Disk template', max_length=32,
95 9ff7ca6f Giorgos Verigakis
            default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
96 2c60cbef Giorgos Verigakis
    deleted = models.BooleanField('Deleted', default=False)
97 ce55f211 Kostas Papadimitriou
98 be7b8d37 Vassilios Karakoidas
    class Meta:
99 dcfc6c2d Vangelis Koukis
        verbose_name = u'Virtual machine flavor'
100 b1af40e2 Giorgos Verigakis
        unique_together = ('cpu', 'ram', 'disk', 'disk_template')
101 ce55f211 Kostas Papadimitriou
102 9ff7ca6f Giorgos Verigakis
    @property
103 9ff7ca6f Giorgos Verigakis
    def name(self):
104 f0f4edac Vassilios Karakoidas
        """Returns flavor name (generated)"""
105 d08a5f6f Vangelis Koukis
        return u'C%dR%dD%d' % (self.cpu, self.ram, self.disk)
106 ce55f211 Kostas Papadimitriou
107 46547e14 Faidon Liambotis
    def __unicode__(self):
108 46547e14 Faidon Liambotis
        return self.name
109 452d2391 Vassilios Karakoidas
110 46547e14 Faidon Liambotis
111 aed9b901 Christos Stavrakakis
class BackendQuerySet(models.query.QuerySet):
112 aed9b901 Christos Stavrakakis
    def delete(self):
113 aed9b901 Christos Stavrakakis
        for backend in self._clone():
114 aed9b901 Christos Stavrakakis
            backend.delete()
115 aed9b901 Christos Stavrakakis
116 aed9b901 Christos Stavrakakis
class ProtectDeleteManager(models.Manager):
117 aed9b901 Christos Stavrakakis
    def get_query_set(self):
118 aed9b901 Christos Stavrakakis
        return BackendQuerySet(self.model, using=self._db)
119 aed9b901 Christos Stavrakakis
120 aed9b901 Christos Stavrakakis
121 aed9b901 Christos Stavrakakis
class Backend(models.Model):
122 aed9b901 Christos Stavrakakis
    clustername = models.CharField('Cluster Name', max_length=128, unique=True)
123 aed9b901 Christos Stavrakakis
    port = models.PositiveIntegerField('Port', default=5080)
124 aed9b901 Christos Stavrakakis
    username = models.CharField('Username', max_length=64, blank=True,
125 aed9b901 Christos Stavrakakis
                                null=True)
126 aed9b901 Christos Stavrakakis
    password = models.CharField('Password', max_length=64, blank=True,
127 aed9b901 Christos Stavrakakis
                                null=True)
128 aed9b901 Christos Stavrakakis
    # Sha1 is up to 40 characters long
129 aed9b901 Christos Stavrakakis
    hash = models.CharField('Hash', max_length=40, editable=False, null=False)
130 aed9b901 Christos Stavrakakis
    drained = models.BooleanField('Drained', default=False, null=False)
131 aed9b901 Christos Stavrakakis
    offline = models.BooleanField('Offline', default=False, null=False)
132 aed9b901 Christos Stavrakakis
    # Last refresh of backend resources
133 aed9b901 Christos Stavrakakis
    updated = models.DateTimeField(auto_now_add=True)
134 aed9b901 Christos Stavrakakis
    # Backend resources
135 aed9b901 Christos Stavrakakis
    mfree = models.PositiveIntegerField('Free Memory', default=0, null=False)
136 aed9b901 Christos Stavrakakis
    mtotal = models.PositiveIntegerField('Total Memory', default=0, null=False)
137 aed9b901 Christos Stavrakakis
    dfree = models.PositiveIntegerField('Free Disk', default=0, null=False)
138 aed9b901 Christos Stavrakakis
    dtotal = models.PositiveIntegerField('Total Disk', default=0, null=False)
139 aed9b901 Christos Stavrakakis
    pinst_cnt = models.PositiveIntegerField('Primary Instances', default=0,
140 aed9b901 Christos Stavrakakis
                                            null=False)
141 aed9b901 Christos Stavrakakis
    ctotal = models.PositiveIntegerField('Total number of logical processors',
142 aed9b901 Christos Stavrakakis
                                         default=0, null=False)
143 aed9b901 Christos Stavrakakis
    # Custom object manager to protect from cascade delete
144 aed9b901 Christos Stavrakakis
    objects = ProtectDeleteManager()
145 aed9b901 Christos Stavrakakis
146 aed9b901 Christos Stavrakakis
    class Meta:
147 aed9b901 Christos Stavrakakis
        verbose_name = u'Backend'
148 aed9b901 Christos Stavrakakis
        ordering = ["clustername"]
149 aed9b901 Christos Stavrakakis
150 aed9b901 Christos Stavrakakis
    def __unicode__(self):
151 aed9b901 Christos Stavrakakis
        return self.clustername
152 aed9b901 Christos Stavrakakis
153 aed9b901 Christos Stavrakakis
    @property
154 aed9b901 Christos Stavrakakis
    def backend_id(self):
155 aed9b901 Christos Stavrakakis
        return self.id
156 aed9b901 Christos Stavrakakis
157 aed9b901 Christos Stavrakakis
    @property
158 aed9b901 Christos Stavrakakis
    def client(self):
159 aed9b901 Christos Stavrakakis
        """Get or create a client. """
160 aed9b901 Christos Stavrakakis
        if not self.offline:
161 aed9b901 Christos Stavrakakis
            return get_client(self.hash, self)
162 aed9b901 Christos Stavrakakis
        else:
163 aed9b901 Christos Stavrakakis
            raise ServiceUnavailable
164 aed9b901 Christos Stavrakakis
165 aed9b901 Christos Stavrakakis
    def create_hash(self):
166 aed9b901 Christos Stavrakakis
        """Create a hash for this backend. """
167 aed9b901 Christos Stavrakakis
        return sha1('%s%s%s%s' % \
168 aed9b901 Christos Stavrakakis
                (self.clustername, self.port, self.username, self.password)) \
169 aed9b901 Christos Stavrakakis
                .hexdigest()
170 aed9b901 Christos Stavrakakis
171 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
172 aed9b901 Christos Stavrakakis
        # Create a new hash each time a Backend is saved
173 aed9b901 Christos Stavrakakis
        old_hash = self.hash
174 aed9b901 Christos Stavrakakis
        self.hash = self.create_hash()
175 aed9b901 Christos Stavrakakis
        super(Backend, self).save(*args, **kwargs)
176 aed9b901 Christos Stavrakakis
        if self.hash != old_hash:
177 aed9b901 Christos Stavrakakis
            # Populate the new hash to the new instances
178 aed9b901 Christos Stavrakakis
            self.virtual_machines.filter(deleted=False).update(backend_hash=self.hash)
179 aed9b901 Christos Stavrakakis
180 aed9b901 Christos Stavrakakis
    def delete(self, *args, **kwargs):
181 aed9b901 Christos Stavrakakis
        # Integrity Error if non-deleted VMs are associated with Backend
182 aed9b901 Christos Stavrakakis
        if self.virtual_machines.filter(deleted=False).count():
183 aed9b901 Christos Stavrakakis
            raise IntegrityError("Non-deleted virtual machines are associated "
184 aed9b901 Christos Stavrakakis
                                 "with backend: %s" % self)
185 aed9b901 Christos Stavrakakis
        else:
186 aed9b901 Christos Stavrakakis
            # ON_DELETE = SET NULL
187 fd65ab41 Christos Stavrakakis
            self.virtual_machines.all().backend = None
188 aed9b901 Christos Stavrakakis
            super(Backend, self).delete(*args, **kwargs)
189 aed9b901 Christos Stavrakakis
190 aed9b901 Christos Stavrakakis
191 fd65ab41 Christos Stavrakakis
# A backend job may be in one of the following possible states
192 fd65ab41 Christos Stavrakakis
BACKEND_STATUSES = (
193 fd65ab41 Christos Stavrakakis
    ('queued', 'request queued'),
194 fd65ab41 Christos Stavrakakis
    ('waiting', 'request waiting for locks'),
195 fd65ab41 Christos Stavrakakis
    ('canceling', 'request being canceled'),
196 fd65ab41 Christos Stavrakakis
    ('running', 'request running'),
197 fd65ab41 Christos Stavrakakis
    ('canceled', 'request canceled'),
198 fd65ab41 Christos Stavrakakis
    ('success', 'request completed successfully'),
199 fd65ab41 Christos Stavrakakis
    ('error', 'request returned error')
200 fd65ab41 Christos Stavrakakis
)
201 fd65ab41 Christos Stavrakakis
202 fd65ab41 Christos Stavrakakis
203 07f3219d Vassilios Karakoidas
class VirtualMachine(models.Model):
204 c92af313 Vangelis Koukis
    # The list of possible actions for a VM
205 d08a5f6f Vangelis Koukis
    ACTIONS = (
206 d08a5f6f Vangelis Koukis
       ('CREATE', 'Create VM'),
207 d08a5f6f Vangelis Koukis
       ('START', 'Start VM'),
208 d08a5f6f Vangelis Koukis
       ('STOP', 'Shutdown VM'),
209 d08a5f6f Vangelis Koukis
       ('SUSPEND', 'Admin Suspend VM'),
210 d08a5f6f Vangelis Koukis
       ('REBOOT', 'Reboot VM'),
211 d08a5f6f Vangelis Koukis
       ('DESTROY', 'Destroy VM')
212 d08a5f6f Vangelis Koukis
    )
213 ce55f211 Kostas Papadimitriou
214 c92af313 Vangelis Koukis
    # The internal operating state of a VM
215 d08a5f6f Vangelis Koukis
    OPER_STATES = (
216 d08a5f6f Vangelis Koukis
        ('BUILD', 'Queued for creation'),
217 d08a5f6f Vangelis Koukis
        ('ERROR', 'Creation failed'),
218 d08a5f6f Vangelis Koukis
        ('STOPPED', 'Stopped'),
219 d08a5f6f Vangelis Koukis
        ('STARTED', 'Started'),
220 d08a5f6f Vangelis Koukis
        ('DESTROYED', 'Destroyed')
221 d08a5f6f Vangelis Koukis
    )
222 ce55f211 Kostas Papadimitriou
223 c92af313 Vangelis Koukis
    # The list of possible operations on the backend
224 d08a5f6f Vangelis Koukis
    BACKEND_OPCODES = (
225 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_CREATE', 'Create Instance'),
226 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_REMOVE', 'Remove Instance'),
227 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_STARTUP', 'Startup Instance'),
228 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_SHUTDOWN', 'Shutdown Instance'),
229 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_REBOOT', 'Reboot Instance'),
230 26fef2b9 Vangelis Koukis
231 26fef2b9 Vangelis Koukis
        # These are listed here for completeness,
232 26fef2b9 Vangelis Koukis
        # and are ignored for the time being
233 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_SET_PARAMS', 'Set Instance Parameters'),
234 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_QUERY_DATA', 'Query Instance Data'),
235 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REINSTALL', 'Reinstall Instance'),
236 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_ACTIVATE_DISKS', 'Activate Disks'),
237 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_DEACTIVATE_DISKS', 'Deactivate Disks'),
238 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REPLACE_DISKS', 'Replace Disks'),
239 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_MIGRATE', 'Migrate Instance'),
240 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_CONSOLE', 'Get Instance Console'),
241 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_RECREATE_DISKS', 'Recreate Disks'),
242 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_FAILOVER', 'Failover Instance')
243 d08a5f6f Vangelis Koukis
    )
244 ce55f211 Kostas Papadimitriou
245 d08a5f6f Vangelis Koukis
246 d08a5f6f Vangelis Koukis
    # The operating state of a VM,
247 d08a5f6f Vangelis Koukis
    # upon the successful completion of a backend operation.
248 21d8adbf Vangelis Koukis
    # IMPORTANT: Make sure all keys have a corresponding
249 21d8adbf Vangelis Koukis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
250 d08a5f6f Vangelis Koukis
    OPER_STATE_FROM_OPCODE = {
251 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_CREATE': 'STARTED',
252 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_REMOVE': 'DESTROYED',
253 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_STARTUP': 'STARTED',
254 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_SHUTDOWN': 'STOPPED',
255 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_REBOOT': 'STARTED',
256 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_SET_PARAMS': None,
257 8490bf84 Georgios Gousios
        'OP_INSTANCE_QUERY_DATA': None,
258 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REINSTALL': None,
259 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_ACTIVATE_DISKS': None,
260 8490bf84 Georgios Gousios
        'OP_INSTANCE_DEACTIVATE_DISKS': None,
261 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REPLACE_DISKS': None,
262 8490bf84 Georgios Gousios
        'OP_INSTANCE_MIGRATE': None,
263 8490bf84 Georgios Gousios
        'OP_INSTANCE_CONSOLE': None,
264 41303ed0 Vangelis Koukis
        'OP_INSTANCE_RECREATE_DISKS': None,
265 41303ed0 Vangelis Koukis
        'OP_INSTANCE_FAILOVER': None
266 d08a5f6f Vangelis Koukis
    }
267 d08a5f6f Vangelis Koukis
268 c92af313 Vangelis Koukis
    # This dictionary contains the correspondence between
269 c92af313 Vangelis Koukis
    # internal operating states and Server States as defined
270 c92af313 Vangelis Koukis
    # by the Rackspace API.
271 d08a5f6f Vangelis Koukis
    RSAPI_STATE_FROM_OPER_STATE = {
272 d08a5f6f Vangelis Koukis
        "BUILD": "BUILD",
273 d08a5f6f Vangelis Koukis
        "ERROR": "ERROR",
274 d08a5f6f Vangelis Koukis
        "STOPPED": "STOPPED",
275 d08a5f6f Vangelis Koukis
        "STARTED": "ACTIVE",
276 d08a5f6f Vangelis Koukis
        "DESTROYED": "DELETED"
277 d08a5f6f Vangelis Koukis
    }
278 d08a5f6f Vangelis Koukis
279 4daac449 Vassilios Karakoidas
    name = models.CharField('Virtual Machine Name', max_length=255)
280 244c552b Giorgos Verigakis
    userid = models.CharField('User ID of the owner', max_length=100)
281 aed9b901 Christos Stavrakakis
    backend = models.ForeignKey(Backend, null=True,
282 aed9b901 Christos Stavrakakis
                                related_name="virtual_machines",)
283 aed9b901 Christos Stavrakakis
    backend_hash = models.CharField(max_length=128, null=True, editable=False)
284 d174b105 Vassilios Karakoidas
    created = models.DateTimeField(auto_now_add=True)
285 d174b105 Vassilios Karakoidas
    updated = models.DateTimeField(auto_now=True)
286 936afb7b Giorgos Verigakis
    imageid = models.CharField(max_length=100, null=False)
287 22571df6 Vassilios Karakoidas
    hostid = models.CharField(max_length=100)
288 46547e14 Faidon Liambotis
    flavor = models.ForeignKey(Flavor)
289 c750d1d6 Vangelis Koukis
    deleted = models.BooleanField('Deleted', default=False)
290 9ff7ca6f Giorgos Verigakis
    suspended = models.BooleanField('Administratively Suspended',
291 244c552b Giorgos Verigakis
                                    default=False)
292 d08a5f6f Vangelis Koukis
293 ce55f211 Kostas Papadimitriou
    # VM State
294 52194743 Vangelis Koukis
    # The following fields are volatile data, in the sense
295 52194743 Vangelis Koukis
    # that they need not be persistent in the DB, but rather
296 52194743 Vangelis Koukis
    # get generated at runtime by quering Ganeti and applying
297 52194743 Vangelis Koukis
    # updates received from Ganeti.
298 ce55f211 Kostas Papadimitriou
299 73b2b65e Vassilios Karakoidas
    # In the future they could be moved to a separate caching layer
300 73b2b65e Vassilios Karakoidas
    # and removed from the database.
301 73b2b65e Vassilios Karakoidas
    # [vkoukis] after discussion with [faidon].
302 dfd19c2d Vassilios Karakoidas
    action = models.CharField(choices=ACTIONS, max_length=30, null=True)
303 dfd19c2d Vassilios Karakoidas
    operstate = models.CharField(choices=OPER_STATES, max_length=30, null=True)
304 dfd19c2d Vassilios Karakoidas
    backendjobid = models.PositiveIntegerField(null=True)
305 f533f224 Vangelis Koukis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
306 aed9b901 Christos Stavrakakis
                                     null=True)
307 9ff7ca6f Giorgos Verigakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
308 aed9b901 Christos Stavrakakis
                                        max_length=30, null=True)
309 dfd19c2d Vassilios Karakoidas
    backendlogmsg = models.TextField(null=True)
310 19da4325 Vangelis Koukis
    buildpercentage = models.IntegerField(default=0)
311 c4e55622 Christos Stavrakakis
    backendtime = models.DateTimeField(default=datetime.datetime.min)
312 9068cd85 Georgios Gousios
313 aed9b901 Christos Stavrakakis
    @property
314 aed9b901 Christos Stavrakakis
    def client(self):
315 f5b4f2a3 Christos Stavrakakis
        if self.backend and not self.backend.offline:
316 aed9b901 Christos Stavrakakis
            return get_client(self.backend_hash, self.backend_id)
317 aed9b901 Christos Stavrakakis
        else:
318 aed9b901 Christos Stavrakakis
            raise ServiceUnavailable
319 aed9b901 Christos Stavrakakis
320 d08a5f6f Vangelis Koukis
    # Error classes
321 d08a5f6f Vangelis Koukis
    class InvalidBackendIdError(Exception):
322 1bce82c0 Christos Stavrakakis
        def __init__(self, value):
323 d08a5f6f Vangelis Koukis
            self.value = value
324 1bce82c0 Christos Stavrakakis
325 1bce82c0 Christos Stavrakakis
        def __str__(self):
326 d08a5f6f Vangelis Koukis
            return repr(self.value)
327 d08a5f6f Vangelis Koukis
328 d08a5f6f Vangelis Koukis
    class InvalidBackendMsgError(Exception):
329 1bce82c0 Christos Stavrakakis
        def __init__(self, opcode, status):
330 d08a5f6f Vangelis Koukis
            self.opcode = opcode
331 d08a5f6f Vangelis Koukis
            self.status = status
332 1bce82c0 Christos Stavrakakis
333 1bce82c0 Christos Stavrakakis
        def __str__(self):
334 9ff7ca6f Giorgos Verigakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
335 1bce82c0 Christos Stavrakakis
                        self.status))
336 d08a5f6f Vangelis Koukis
337 d08a5f6f Vangelis Koukis
    class InvalidActionError(Exception):
338 1bce82c0 Christos Stavrakakis
        def __init__(self, action):
339 c92af313 Vangelis Koukis
            self._action = action
340 1bce82c0 Christos Stavrakakis
341 1bce82c0 Christos Stavrakakis
        def __str__(self):
342 78d9b6b7 Vangelis Koukis
            return repr(str(self._action))
343 ce55f211 Kostas Papadimitriou
344 5231a38a Giorgos Verigakis
    class DeletedError(Exception):
345 5231a38a Giorgos Verigakis
        pass
346 ce55f211 Kostas Papadimitriou
347 5231a38a Giorgos Verigakis
    class BuildingError(Exception):
348 5231a38a Giorgos Verigakis
        pass
349 ce55f211 Kostas Papadimitriou
350 3846dfd0 Vassilios Karakoidas
    def __init__(self, *args, **kw):
351 d08a5f6f Vangelis Koukis
        """Initialize state for just created VM instances."""
352 d08a5f6f Vangelis Koukis
        super(VirtualMachine, self).__init__(*args, **kw)
353 bc9c872b Vangelis Koukis
        # This gets called BEFORE an instance gets save()d for
354 bc9c872b Vangelis Koukis
        # the first time.
355 ce55f211 Kostas Papadimitriou
        if not self.pk:
356 dfd19c2d Vassilios Karakoidas
            self.action = None
357 dfd19c2d Vassilios Karakoidas
            self.backendjobid = None
358 dfd19c2d Vassilios Karakoidas
            self.backendjobstatus = None
359 dfd19c2d Vassilios Karakoidas
            self.backendopcode = None
360 dfd19c2d Vassilios Karakoidas
            self.backendlogmsg = None
361 dfd19c2d Vassilios Karakoidas
            self.operstate = 'BUILD'
362 ce55f211 Kostas Papadimitriou
363 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
364 aed9b901 Christos Stavrakakis
        # Store hash for first time saved vm
365 aed9b901 Christos Stavrakakis
        if (self.id is None or self.backend_hash == '') and self.backend:
366 aed9b901 Christos Stavrakakis
            self.backend_hash = self.backend.hash
367 aed9b901 Christos Stavrakakis
        super(VirtualMachine, self).save(*args, **kwargs)
368 aed9b901 Christos Stavrakakis
369 9ff7ca6f Giorgos Verigakis
    @property
370 924d8085 Christos Stavrakakis
    def backend_vm_id(self):
371 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
372 76ba77c1 Vangelis Koukis
        if not self.id:
373 76ba77c1 Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError("self.id is None")
374 d30f29aa Christos Stavrakakis
        return "%s%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
375 ce55f211 Kostas Papadimitriou
376 d08a5f6f Vangelis Koukis
    class Meta:
377 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
378 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
379 ce55f211 Kostas Papadimitriou
380 d08a5f6f Vangelis Koukis
    def __unicode__(self):
381 d08a5f6f Vangelis Koukis
        return self.name
382 d08a5f6f Vangelis Koukis
383 4ffb82dc Vassilios Karakoidas
384 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
385 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
386 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
387 7cc3c7d9 Giorgos Verigakis
    vm = models.ForeignKey(VirtualMachine, related_name='metadata')
388 ce55f211 Kostas Papadimitriou
389 be7b8d37 Vassilios Karakoidas
    class Meta:
390 1bd24df6 Giorgos Verigakis
        unique_together = (('meta_key', 'vm'),)
391 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
392 ce55f211 Kostas Papadimitriou
393 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
394 9ff7ca6f Giorgos Verigakis
        return u'%s: %s' % (self.meta_key, self.meta_value)
395 be7b8d37 Vassilios Karakoidas
396 be7b8d37 Vassilios Karakoidas
397 e6a42a96 Christos Stavrakakis
398 0269afd6 Giorgos Verigakis
class Network(models.Model):
399 e6a42a96 Christos Stavrakakis
    OPER_STATES = (
400 e6a42a96 Christos Stavrakakis
        ('PENDING', 'Pending'),
401 f533f224 Vangelis Koukis
        ('ACTIVE', 'Active'),
402 e6a42a96 Christos Stavrakakis
        ('DELETED', 'Deleted'),
403 e6a42a96 Christos Stavrakakis
        ('ERROR', 'Error')
404 e6a42a96 Christos Stavrakakis
    )
405 e6a42a96 Christos Stavrakakis
406 e6a42a96 Christos Stavrakakis
    ACTIONS = (
407 e6a42a96 Christos Stavrakakis
       ('CREATE', 'Create Network'),
408 e6a42a96 Christos Stavrakakis
       ('DESTROY', 'Destroy Network'),
409 e6a42a96 Christos Stavrakakis
    )
410 e6a42a96 Christos Stavrakakis
411 e6a42a96 Christos Stavrakakis
    RSAPI_STATE_FROM_OPER_STATE = {
412 e6a42a96 Christos Stavrakakis
        'PENDING': 'PENDING',
413 e6a42a96 Christos Stavrakakis
        'ACTIVE': 'ACTIVE',
414 e6a42a96 Christos Stavrakakis
        'DELETED': 'DELETED',
415 e6a42a96 Christos Stavrakakis
        'ERROR': 'ERROR'
416 e6a42a96 Christos Stavrakakis
    }
417 e6a42a96 Christos Stavrakakis
418 e6a42a96 Christos Stavrakakis
    NETWORK_TYPES = (
419 e6a42a96 Christos Stavrakakis
        ('PUBLIC_ROUTED', 'Public routed network'),
420 e6a42a96 Christos Stavrakakis
        ('PRIVATE_VLAN', 'Private vlan network'),
421 e6a42a96 Christos Stavrakakis
        ('PRIVATE_FILTERED', 'Private network with mac-filtering')
422 f533f224 Vangelis Koukis
    )
423 ce55f211 Kostas Papadimitriou
424 e6a42a96 Christos Stavrakakis
    NETWORK_TAGS = {
425 fd65ab41 Christos Stavrakakis
        'PUBLIC_ROUTED': ['ip-less-routed'],
426 fd65ab41 Christos Stavrakakis
        'PRIVATE_VLAN': ['physical-vlan'],
427 fd65ab41 Christos Stavrakakis
        'PRIVATE_FILTERED': ['mac-filtered']
428 e6a42a96 Christos Stavrakakis
    }
429 e6a42a96 Christos Stavrakakis
430 e6a42a96 Christos Stavrakakis
    name = models.CharField('Network Name', max_length=128)
431 e6a42a96 Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=128, null=True)
432 e6a42a96 Christos Stavrakakis
    subnet = models.CharField('Subnet', max_length=32, default='10.0.0.0/24')
433 e6a42a96 Christos Stavrakakis
    gateway = models.CharField('Gateway', max_length=32, null=True)
434 e6a42a96 Christos Stavrakakis
    dhcp = models.BooleanField('DHCP', default=True)
435 e6a42a96 Christos Stavrakakis
    type = models.CharField(choices=NETWORK_TYPES, max_length=50, default='PRIVATE_VLAN')
436 e6a42a96 Christos Stavrakakis
    link = models.CharField('Network Link', max_length=128, null=True)
437 fd65ab41 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=True)
438 e6a42a96 Christos Stavrakakis
    public = models.BooleanField(default=False)
439 0269afd6 Giorgos Verigakis
    created = models.DateTimeField(auto_now_add=True)
440 0269afd6 Giorgos Verigakis
    updated = models.DateTimeField(auto_now=True)
441 e6a42a96 Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False)
442 fd65ab41 Christos Stavrakakis
    state = models.CharField(choices=OPER_STATES, max_length=32,
443 fd65ab41 Christos Stavrakakis
                             default='PENDING')
444 f533f224 Vangelis Koukis
    machines = models.ManyToManyField(VirtualMachine,
445 244c552b Giorgos Verigakis
                                      through='NetworkInterface')
446 fd65ab41 Christos Stavrakakis
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
447 fd65ab41 Christos Stavrakakis
                              default=None)
448 e6a42a96 Christos Stavrakakis
449 e6a42a96 Christos Stavrakakis
    class InvalidBackendIdError(Exception):
450 fd65ab41 Christos Stavrakakis
        def __init__(self, value):
451 e6a42a96 Christos Stavrakakis
            self.value = value
452 fd65ab41 Christos Stavrakakis
453 fd65ab41 Christos Stavrakakis
        def __str__(self):
454 e6a42a96 Christos Stavrakakis
            return repr(self.value)
455 e6a42a96 Christos Stavrakakis
456 e6a42a96 Christos Stavrakakis
457 e6a42a96 Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
458 fd65ab41 Christos Stavrakakis
        def __init__(self, opcode, status):
459 e6a42a96 Christos Stavrakakis
            self.opcode = opcode
460 e6a42a96 Christos Stavrakakis
            self.status = status
461 fd65ab41 Christos Stavrakakis
462 fd65ab41 Christos Stavrakakis
        def __str__(self):
463 e6a42a96 Christos Stavrakakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
464 e6a42a96 Christos Stavrakakis
                    self.status))
465 e6a42a96 Christos Stavrakakis
466 e6a42a96 Christos Stavrakakis
    class InvalidActionError(Exception):
467 fd65ab41 Christos Stavrakakis
        def __init__(self, action):
468 e6a42a96 Christos Stavrakakis
            self._action = action
469 e6a42a96 Christos Stavrakakis
470 fd65ab41 Christos Stavrakakis
        def __str__(self):
471 fd65ab41 Christos Stavrakakis
            return repr(str(self._action))
472 e6a42a96 Christos Stavrakakis
473 e6a42a96 Christos Stavrakakis
    @property
474 e6a42a96 Christos Stavrakakis
    def backend_id(self):
475 fd65ab41 Christos Stavrakakis
        """Return the backend id by prepending backend-prefix."""
476 e6a42a96 Christos Stavrakakis
        if not self.id:
477 e6a42a96 Christos Stavrakakis
            raise Network.InvalidBackendIdError("self.id is None")
478 d30f29aa Christos Stavrakakis
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
479 e6a42a96 Christos Stavrakakis
480 e6a42a96 Christos Stavrakakis
    @property
481 e6a42a96 Christos Stavrakakis
    def backend_tag(self):
482 e6a42a96 Christos Stavrakakis
        """Return the network tag to be used in backend
483 e6a42a96 Christos Stavrakakis

484 e6a42a96 Christos Stavrakakis
        """
485 e6a42a96 Christos Stavrakakis
        return Network.NETWORK_TAGS[self.type]
486 e6a42a96 Christos Stavrakakis
487 0269afd6 Giorgos Verigakis
    def __unicode__(self):
488 0269afd6 Giorgos Verigakis
        return self.name
489 4f8b91cd Georgios Gousios
490 fd65ab41 Christos Stavrakakis
    def update_oper_state(self):
491 fd65ab41 Christos Stavrakakis
        """Update operstate of the Network.
492 fd65ab41 Christos Stavrakakis

493 fd65ab41 Christos Stavrakakis
        Update the operstate of the Network depending on the related
494 fd65ab41 Christos Stavrakakis
        backend_networks. When backend networks do not have the same operstate,
495 fd65ab41 Christos Stavrakakis
        the Network's operstate is PENDING. Otherwise it is the same with
496 fd65ab41 Christos Stavrakakis
        the BackendNetworks operstate.
497 fd65ab41 Christos Stavrakakis

498 fd65ab41 Christos Stavrakakis
        """
499 fd65ab41 Christos Stavrakakis
        backend_states = [s.operstate for s in self.backend_networks.all()]
500 fd65ab41 Christos Stavrakakis
        if not backend_states:
501 fd65ab41 Christos Stavrakakis
            self.operstate = 'PENDING'
502 fd65ab41 Christos Stavrakakis
            self.save()
503 fd65ab41 Christos Stavrakakis
            return
504 fd65ab41 Christos Stavrakakis
505 fd65ab41 Christos Stavrakakis
        all_equal = len(set(backend_states)) <= 1
506 fd65ab41 Christos Stavrakakis
        self.operstate = all_equal and backend_states[0] or 'PENDING'
507 fd65ab41 Christos Stavrakakis
508 fd65ab41 Christos Stavrakakis
        if self.operstate == 'DELETED':
509 fd65ab41 Christos Stavrakakis
            self.deleted = True
510 fd65ab41 Christos Stavrakakis
511 fd65ab41 Christos Stavrakakis
        self.save()
512 fd65ab41 Christos Stavrakakis
513 fd65ab41 Christos Stavrakakis
    def save(self, *args, **kwargs):
514 fd65ab41 Christos Stavrakakis
        pk = self.pk
515 fd65ab41 Christos Stavrakakis
        super(Network, self).save(*args, **kwargs)
516 fd65ab41 Christos Stavrakakis
        if not pk:
517 fd65ab41 Christos Stavrakakis
            # In case of a new Network, corresponding BackendNetwork's must
518 fd65ab41 Christos Stavrakakis
            # be created!
519 fd65ab41 Christos Stavrakakis
            for back in Backend.objects.all():
520 fd65ab41 Christos Stavrakakis
                BackendNetwork.objects.create(backend=back, network=self)
521 fd65ab41 Christos Stavrakakis
522 fd65ab41 Christos Stavrakakis
523 fd65ab41 Christos Stavrakakis
class BackendNetwork(models.Model):
524 fd65ab41 Christos Stavrakakis
    OPER_STATES = (
525 fd65ab41 Christos Stavrakakis
        ('PENDING', 'Pending'),
526 fd65ab41 Christos Stavrakakis
        ('ACTIVE', 'Active'),
527 fd65ab41 Christos Stavrakakis
        ('DELETED', 'Deleted'),
528 fd65ab41 Christos Stavrakakis
        ('ERROR', 'Error')
529 fd65ab41 Christos Stavrakakis
    )
530 fd65ab41 Christos Stavrakakis
531 fd65ab41 Christos Stavrakakis
    # The list of possible operations on the backend
532 fd65ab41 Christos Stavrakakis
    BACKEND_OPCODES = (
533 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_ADD', 'Create Network'),
534 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_CONNECT', 'Activate Network'),
535 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_DISCONNECT', 'Deactivate Network'),
536 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_REMOVE', 'Remove Network'),
537 fd65ab41 Christos Stavrakakis
        # These are listed here for completeness,
538 fd65ab41 Christos Stavrakakis
        # and are ignored for the time being
539 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_SET_PARAMS', 'Set Network Parameters'),
540 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_QUERY_DATA', 'Query Network Data')
541 fd65ab41 Christos Stavrakakis
    )
542 fd65ab41 Christos Stavrakakis
543 fd65ab41 Christos Stavrakakis
    # The operating state of a Netowork,
544 fd65ab41 Christos Stavrakakis
    # upon the successful completion of a backend operation.
545 fd65ab41 Christos Stavrakakis
    # IMPORTANT: Make sure all keys have a corresponding
546 fd65ab41 Christos Stavrakakis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
547 fd65ab41 Christos Stavrakakis
    OPER_STATE_FROM_OPCODE = {
548 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_ADD': 'PENDING',
549 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_CONNECT': 'ACTIVE',
550 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_DISCONNECT': 'PENDING',
551 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_REMOVE': 'DELETED',
552 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_SET_PARAMS': None,
553 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_QUERY_DATA': None
554 fd65ab41 Christos Stavrakakis
    }
555 fd65ab41 Christos Stavrakakis
556 fd65ab41 Christos Stavrakakis
    network = models.ForeignKey(Network, related_name='backend_networks')
557 fd65ab41 Christos Stavrakakis
    backend = models.ForeignKey(Backend, related_name='networks')
558 fd65ab41 Christos Stavrakakis
    created = models.DateTimeField(auto_now_add=True)
559 fd65ab41 Christos Stavrakakis
    updated = models.DateTimeField(auto_now=True)
560 fd65ab41 Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False)
561 fd65ab41 Christos Stavrakakis
    operstate = models.CharField(choices=OPER_STATES, max_length=30,
562 fd65ab41 Christos Stavrakakis
                                 default='PENDING')
563 fd65ab41 Christos Stavrakakis
    backendjobid = models.PositiveIntegerField(null=True)
564 fd65ab41 Christos Stavrakakis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
565 fd65ab41 Christos Stavrakakis
                                     null=True)
566 fd65ab41 Christos Stavrakakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
567 fd65ab41 Christos Stavrakakis
                                        max_length=30, null=True)
568 fd65ab41 Christos Stavrakakis
    backendlogmsg = models.TextField(null=True)
569 fd65ab41 Christos Stavrakakis
    backendtime = models.DateTimeField(null=False,
570 fd65ab41 Christos Stavrakakis
                                       default=datetime.datetime.min)
571 fd65ab41 Christos Stavrakakis
572 fd65ab41 Christos Stavrakakis
    def save(self, *args, **kwargs):
573 fd65ab41 Christos Stavrakakis
        super(BackendNetwork, self).save(*args, **kwargs)
574 fd65ab41 Christos Stavrakakis
        self.network.update_oper_state()
575 fd65ab41 Christos Stavrakakis
576 fd65ab41 Christos Stavrakakis
    def delete(self, *args, **kwargs):
577 fd65ab41 Christos Stavrakakis
        super(BackendNetwork, self).delete(*args, **kwargs)
578 fd65ab41 Christos Stavrakakis
        self.network.update_oper_state()
579 fd65ab41 Christos Stavrakakis
580 f533f224 Vangelis Koukis
581 f533f224 Vangelis Koukis
class NetworkInterface(models.Model):
582 f533f224 Vangelis Koukis
    FIREWALL_PROFILES = (
583 f533f224 Vangelis Koukis
        ('ENABLED', 'Enabled'),
584 26563957 Giorgos Verigakis
        ('DISABLED', 'Disabled'),
585 26563957 Giorgos Verigakis
        ('PROTECTED', 'Protected')
586 f533f224 Vangelis Koukis
    )
587 ce55f211 Kostas Papadimitriou
588 f533f224 Vangelis Koukis
    machine = models.ForeignKey(VirtualMachine, related_name='nics')
589 f533f224 Vangelis Koukis
    network = models.ForeignKey(Network, related_name='nics')
590 f533f224 Vangelis Koukis
    created = models.DateTimeField(auto_now_add=True)
591 f533f224 Vangelis Koukis
    updated = models.DateTimeField(auto_now=True)
592 f533f224 Vangelis Koukis
    index = models.IntegerField(null=True)
593 f533f224 Vangelis Koukis
    mac = models.CharField(max_length=17, null=True)
594 f533f224 Vangelis Koukis
    ipv4 = models.CharField(max_length=15, null=True)
595 f533f224 Vangelis Koukis
    ipv6 = models.CharField(max_length=100, null=True)
596 f533f224 Vangelis Koukis
    firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
597 244c552b Giorgos Verigakis
                                        max_length=30, null=True)
598 ce55f211 Kostas Papadimitriou
599 2987cd8a Giorgos Verigakis
    def __unicode__(self):
600 2987cd8a Giorgos Verigakis
        return '%s@%s' % (self.machine.name, self.network.name)
601 f533f224 Vangelis Koukis
602 f533f224 Vangelis Koukis
603 e6a42a96 Christos Stavrakakis
class Pool(models.Model):
604 e6a42a96 Christos Stavrakakis
    available = models.BooleanField(default=True, null=False)
605 e6a42a96 Christos Stavrakakis
    index = models.IntegerField(null=False, unique=True)
606 e6a42a96 Christos Stavrakakis
    value = models.CharField(max_length=128, null=False, unique=True)
607 e6a42a96 Christos Stavrakakis
    max_index = 0
608 ce55f211 Kostas Papadimitriou
609 e6a42a96 Christos Stavrakakis
    class Meta:
610 e6a42a96 Christos Stavrakakis
        abstract = True
611 e6a42a96 Christos Stavrakakis
        ordering = ['index']
612 e6a42a96 Christos Stavrakakis
613 e6a42a96 Christos Stavrakakis
    @classmethod
614 e6a42a96 Christos Stavrakakis
    def get_available(cls):
615 e6a42a96 Christos Stavrakakis
        try:
616 e6a42a96 Christos Stavrakakis
            entry = cls.objects.filter(available=True)[0]
617 e6a42a96 Christos Stavrakakis
            entry.available = False
618 e6a42a96 Christos Stavrakakis
            entry.save()
619 e6a42a96 Christos Stavrakakis
            return entry
620 e6a42a96 Christos Stavrakakis
        except IndexError:
621 e6a42a96 Christos Stavrakakis
            return cls.generate_new()
622 e6a42a96 Christos Stavrakakis
623 e6a42a96 Christos Stavrakakis
    @classmethod
624 e6a42a96 Christos Stavrakakis
    def generate_new(cls):
625 e6a42a96 Christos Stavrakakis
        try:
626 e6a42a96 Christos Stavrakakis
            last = cls.objects.order_by('-index')[0]
627 e6a42a96 Christos Stavrakakis
            index = last.index + 1
628 e6a42a96 Christos Stavrakakis
        except IndexError:
629 e6a42a96 Christos Stavrakakis
            index = 1
630 e6a42a96 Christos Stavrakakis
631 e6a42a96 Christos Stavrakakis
        if index <= cls.max_index:
632 e6a42a96 Christos Stavrakakis
            return cls.objects.create(index=index,
633 e6a42a96 Christos Stavrakakis
                                      value=cls.value_from_index(index),
634 e6a42a96 Christos Stavrakakis
                                      available=False)
635 e6a42a96 Christos Stavrakakis
636 e6a42a96 Christos Stavrakakis
        raise Pool.PoolExhausted()
637 e6a42a96 Christos Stavrakakis
638 e6a42a96 Christos Stavrakakis
    @classmethod
639 e6a42a96 Christos Stavrakakis
    def set_available(cls, value):
640 e6a42a96 Christos Stavrakakis
        entry = cls.objects.get(value=value)
641 e6a42a96 Christos Stavrakakis
        entry.available = True
642 e6a42a96 Christos Stavrakakis
        entry.save()
643 e6a42a96 Christos Stavrakakis
644 e6a42a96 Christos Stavrakakis
645 e6a42a96 Christos Stavrakakis
    class PoolExhausted(Exception):
646 ce55f211 Kostas Papadimitriou
        pass
647 ce55f211 Kostas Papadimitriou
648 e6a42a96 Christos Stavrakakis
649 e6a42a96 Christos Stavrakakis
class BridgePool(Pool):
650 e6a42a96 Christos Stavrakakis
    max_index = snf_settings.GANETI_MAX_LINK_NUMBER
651 e6a42a96 Christos Stavrakakis
652 e6a42a96 Christos Stavrakakis
    @staticmethod
653 e6a42a96 Christos Stavrakakis
    def value_from_index(index):
654 e6a42a96 Christos Stavrakakis
        return snf_settings.GANETI_LINK_PREFIX + str(index)
655 e6a42a96 Christos Stavrakakis
656 e6a42a96 Christos Stavrakakis
657 e6a42a96 Christos Stavrakakis
class MacPrefixPool(Pool):
658 e6a42a96 Christos Stavrakakis
    max_index = snf_settings.GANETI_MAX_MAC_PREFIX_NUMBER
659 e6a42a96 Christos Stavrakakis
660 e6a42a96 Christos Stavrakakis
    @staticmethod
661 e6a42a96 Christos Stavrakakis
    def value_from_index(index):
662 e6a42a96 Christos Stavrakakis
        """Convert number to mac prefix
663 e6a42a96 Christos Stavrakakis

664 e6a42a96 Christos Stavrakakis
        """
665 e6a42a96 Christos Stavrakakis
        high = snf_settings.GANETI_BASE_MAC_PREFIX
666 e6a42a96 Christos Stavrakakis
        a = hex(int(high.replace(":", ""), 16) + index).replace("0x", '')
667 e6a42a96 Christos Stavrakakis
        mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)])
668 e6a42a96 Christos Stavrakakis
        return mac_prefix