Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (24 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 30e0ed74 Christos Stavrakakis
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield
41 aed9b901 Christos Stavrakakis
42 30e0ed74 Christos Stavrakakis
BACKEND_CLIENTS = {}  # {hash:Backend client}
43 30e0ed74 Christos Stavrakakis
BACKEND_HASHES = {}   # {Backend.id:hash}
44 aed9b901 Christos Stavrakakis
45 aed9b901 Christos Stavrakakis
46 aed9b901 Christos Stavrakakis
def get_client(hash, backend):
47 aed9b901 Christos Stavrakakis
    """Get a cached backend client or create a new one.
48 aed9b901 Christos Stavrakakis

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

497 e6a42a96 Christos Stavrakakis
        """
498 839e2bd0 Christos Stavrakakis
        return getattr(snf_settings, self.type + '_TAGS')
499 e6a42a96 Christos Stavrakakis
500 0269afd6 Giorgos Verigakis
    def __unicode__(self):
501 0269afd6 Giorgos Verigakis
        return self.name
502 4f8b91cd Georgios Gousios
503 0b827aa4 Christos Stavrakakis
    def update_state(self):
504 0b827aa4 Christos Stavrakakis
        """Update state of the Network.
505 fd65ab41 Christos Stavrakakis

506 0b827aa4 Christos Stavrakakis
        Update the state of the Network depending on the related
507 fd65ab41 Christos Stavrakakis
        backend_networks. When backend networks do not have the same operstate,
508 0b827aa4 Christos Stavrakakis
        the Network's state is PENDING. Otherwise it is the same with
509 fd65ab41 Christos Stavrakakis
        the BackendNetworks operstate.
510 fd65ab41 Christos Stavrakakis

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

684 e6a42a96 Christos Stavrakakis
        """
685 839e2bd0 Christos Stavrakakis
        high = snf_settings.PRIVATE_MAC_FILTERED_BASE_MAC_PREFIX
686 e6a42a96 Christos Stavrakakis
        a = hex(int(high.replace(":", ""), 16) + index).replace("0x", '')
687 e6a42a96 Christos Stavrakakis
        mac_prefix = ":".join([a[x:x + 2] for x in xrange(0, len(a), 2)])
688 e6a42a96 Christos Stavrakakis
        return mac_prefix