Statistics
| Branch: | Tag: | Revision:

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

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

512 e6a42a96 Christos Stavrakakis
        """
513 b7d38981 Dimitris Aragiorgis
        if self.tags:
514 b7d38981 Dimitris Aragiorgis
            return self.tags.split(',')
515 b7d38981 Dimitris Aragiorgis
        else:
516 b7d38981 Dimitris Aragiorgis
            return []
517 e6a42a96 Christos Stavrakakis
518 7cfbbf32 Christos Stavrakakis
    def create_backend_network(self, backend=None):
519 7cfbbf32 Christos Stavrakakis
        """Create corresponding BackendNetwork entries."""
520 7cfbbf32 Christos Stavrakakis
521 7cfbbf32 Christos Stavrakakis
        backends = [backend] if backend else Backend.objects.all()
522 7cfbbf32 Christos Stavrakakis
        for backend in backends:
523 7cfbbf32 Christos Stavrakakis
            BackendNetwork.objects.create(backend=backend, network=self)
524 fd65ab41 Christos Stavrakakis
525 fdc94944 Christos Stavrakakis
    def get_pool(self):
526 3718c0f1 Christos Stavrakakis
        if not self.pool_id:
527 3718c0f1 Christos Stavrakakis
            self.pool = IPPoolTable.objects.create(available_map='',
528 3718c0f1 Christos Stavrakakis
                                                   reserved_map='',
529 3718c0f1 Christos Stavrakakis
                                                   size=0)
530 3718c0f1 Christos Stavrakakis
            self.save()
531 048fab1e Christos Stavrakakis
        return IPPoolTable.objects.select_for_update().get(id=self.pool_id)\
532 048fab1e Christos Stavrakakis
                                                      .pool
533 77f0fa63 Christos Stavrakakis
534 fdc94944 Christos Stavrakakis
    def reserve_address(self, address):
535 fdc94944 Christos Stavrakakis
        pool = self.get_pool()
536 77f0fa63 Christos Stavrakakis
        pool.reserve(address)
537 fdc94944 Christos Stavrakakis
        pool.save()
538 77f0fa63 Christos Stavrakakis
539 fdc94944 Christos Stavrakakis
    def release_address(self, address):
540 fdc94944 Christos Stavrakakis
        pool = self.get_pool()
541 fdc94944 Christos Stavrakakis
        pool.put(address)
542 fdc94944 Christos Stavrakakis
        pool.save()
543 77f0fa63 Christos Stavrakakis
544 048fab1e Christos Stavrakakis
    class InvalidBackendIdError(Exception):
545 048fab1e Christos Stavrakakis
        def __init__(self, value):
546 048fab1e Christos Stavrakakis
            self.value = value
547 048fab1e Christos Stavrakakis
548 048fab1e Christos Stavrakakis
        def __str__(self):
549 048fab1e Christos Stavrakakis
            return repr(self.value)
550 048fab1e Christos Stavrakakis
551 048fab1e Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
552 048fab1e Christos Stavrakakis
        def __init__(self, opcode, status):
553 048fab1e Christos Stavrakakis
            self.opcode = opcode
554 048fab1e Christos Stavrakakis
            self.status = status
555 048fab1e Christos Stavrakakis
556 048fab1e Christos Stavrakakis
        def __str__(self):
557 048fab1e Christos Stavrakakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
558 048fab1e Christos Stavrakakis
                    self.status))
559 048fab1e Christos Stavrakakis
560 048fab1e Christos Stavrakakis
    class InvalidActionError(Exception):
561 048fab1e Christos Stavrakakis
        def __init__(self, action):
562 048fab1e Christos Stavrakakis
            self._action = action
563 048fab1e Christos Stavrakakis
564 048fab1e Christos Stavrakakis
        def __str__(self):
565 048fab1e Christos Stavrakakis
            return repr(str(self._action))
566 048fab1e Christos Stavrakakis
567 e3187d7a Christos Stavrakakis
    class DeletedError(Exception):
568 e3187d7a Christos Stavrakakis
        pass
569 e3187d7a Christos Stavrakakis
570 e3187d7a Christos Stavrakakis
    class BuildingError(Exception):
571 e3187d7a Christos Stavrakakis
        pass
572 e3187d7a Christos Stavrakakis
573 864bed43 Christos Stavrakakis
574 fd65ab41 Christos Stavrakakis
class BackendNetwork(models.Model):
575 fd65ab41 Christos Stavrakakis
    OPER_STATES = (
576 fd65ab41 Christos Stavrakakis
        ('PENDING', 'Pending'),
577 fd65ab41 Christos Stavrakakis
        ('ACTIVE', 'Active'),
578 fd65ab41 Christos Stavrakakis
        ('DELETED', 'Deleted'),
579 fd65ab41 Christos Stavrakakis
        ('ERROR', 'Error')
580 fd65ab41 Christos Stavrakakis
    )
581 fd65ab41 Christos Stavrakakis
582 fd65ab41 Christos Stavrakakis
    # The list of possible operations on the backend
583 fd65ab41 Christos Stavrakakis
    BACKEND_OPCODES = (
584 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_ADD', 'Create Network'),
585 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_CONNECT', 'Activate Network'),
586 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_DISCONNECT', 'Deactivate Network'),
587 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_REMOVE', 'Remove Network'),
588 fd65ab41 Christos Stavrakakis
        # These are listed here for completeness,
589 fd65ab41 Christos Stavrakakis
        # and are ignored for the time being
590 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_SET_PARAMS', 'Set Network Parameters'),
591 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_QUERY_DATA', 'Query Network Data')
592 fd65ab41 Christos Stavrakakis
    )
593 fd65ab41 Christos Stavrakakis
594 fd65ab41 Christos Stavrakakis
    # The operating state of a Netowork,
595 fd65ab41 Christos Stavrakakis
    # upon the successful completion of a backend operation.
596 fd65ab41 Christos Stavrakakis
    # IMPORTANT: Make sure all keys have a corresponding
597 fd65ab41 Christos Stavrakakis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
598 fd65ab41 Christos Stavrakakis
    OPER_STATE_FROM_OPCODE = {
599 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_ADD': 'PENDING',
600 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_CONNECT': 'ACTIVE',
601 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_DISCONNECT': 'PENDING',
602 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_REMOVE': 'DELETED',
603 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_SET_PARAMS': None,
604 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_QUERY_DATA': None
605 fd65ab41 Christos Stavrakakis
    }
606 fd65ab41 Christos Stavrakakis
607 fd65ab41 Christos Stavrakakis
    network = models.ForeignKey(Network, related_name='backend_networks')
608 fd65ab41 Christos Stavrakakis
    backend = models.ForeignKey(Backend, related_name='networks')
609 fd65ab41 Christos Stavrakakis
    created = models.DateTimeField(auto_now_add=True)
610 fd65ab41 Christos Stavrakakis
    updated = models.DateTimeField(auto_now=True)
611 fd65ab41 Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False)
612 3165f027 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
613 fd65ab41 Christos Stavrakakis
    operstate = models.CharField(choices=OPER_STATES, max_length=30,
614 fd65ab41 Christos Stavrakakis
                                 default='PENDING')
615 fd65ab41 Christos Stavrakakis
    backendjobid = models.PositiveIntegerField(null=True)
616 fd65ab41 Christos Stavrakakis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
617 fd65ab41 Christos Stavrakakis
                                     null=True)
618 fd65ab41 Christos Stavrakakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
619 fd65ab41 Christos Stavrakakis
                                        max_length=30, null=True)
620 fd65ab41 Christos Stavrakakis
    backendlogmsg = models.TextField(null=True)
621 fd65ab41 Christos Stavrakakis
    backendtime = models.DateTimeField(null=False,
622 fd65ab41 Christos Stavrakakis
                                       default=datetime.datetime.min)
623 fd65ab41 Christos Stavrakakis
624 7cfbbf32 Christos Stavrakakis
    class Meta:
625 7cfbbf32 Christos Stavrakakis
        # Ensure one entry for each network in each backend
626 7cfbbf32 Christos Stavrakakis
        unique_together = (("network", "backend"))
627 7cfbbf32 Christos Stavrakakis
628 3165f027 Christos Stavrakakis
    def __init__(self, *args, **kwargs):
629 3165f027 Christos Stavrakakis
        """Initialize state for just created BackendNetwork instances."""
630 3165f027 Christos Stavrakakis
        super(BackendNetwork, self).__init__(*args, **kwargs)
631 3165f027 Christos Stavrakakis
        if not self.mac_prefix:
632 3165f027 Christos Stavrakakis
            # Generate the MAC prefix of the BackendNetwork, by combining
633 3165f027 Christos Stavrakakis
            # the Network prefix with the index of the Backend
634 3165f027 Christos Stavrakakis
            net_prefix = self.network.mac_prefix
635 3165f027 Christos Stavrakakis
            backend_suffix = hex(self.backend.index).replace('0x', '')
636 3165f027 Christos Stavrakakis
            mac_prefix = net_prefix + backend_suffix
637 3165f027 Christos Stavrakakis
            try:
638 3165f027 Christos Stavrakakis
                utils.validate_mac(mac_prefix + ":00:00:00")
639 3165f027 Christos Stavrakakis
            except utils.InvalidMacAddress:
640 3165f027 Christos Stavrakakis
                raise utils.InvalidMacAddress("Invalid MAC prefix '%s'" % \
641 3165f027 Christos Stavrakakis
                                               mac_prefix)
642 3165f027 Christos Stavrakakis
            self.mac_prefix = mac_prefix
643 3165f027 Christos Stavrakakis
644 f533f224 Vangelis Koukis
645 f533f224 Vangelis Koukis
class NetworkInterface(models.Model):
646 f533f224 Vangelis Koukis
    FIREWALL_PROFILES = (
647 f533f224 Vangelis Koukis
        ('ENABLED', 'Enabled'),
648 26563957 Giorgos Verigakis
        ('DISABLED', 'Disabled'),
649 26563957 Giorgos Verigakis
        ('PROTECTED', 'Protected')
650 f533f224 Vangelis Koukis
    )
651 ce55f211 Kostas Papadimitriou
652 f533f224 Vangelis Koukis
    machine = models.ForeignKey(VirtualMachine, related_name='nics')
653 f533f224 Vangelis Koukis
    network = models.ForeignKey(Network, related_name='nics')
654 f533f224 Vangelis Koukis
    created = models.DateTimeField(auto_now_add=True)
655 f533f224 Vangelis Koukis
    updated = models.DateTimeField(auto_now=True)
656 3b56bbe4 Christos Stavrakakis
    index = models.IntegerField(null=False)
657 3165f027 Christos Stavrakakis
    mac = models.CharField(max_length=32, null=False, unique=True)
658 f533f224 Vangelis Koukis
    ipv4 = models.CharField(max_length=15, null=True)
659 f533f224 Vangelis Koukis
    ipv6 = models.CharField(max_length=100, null=True)
660 f533f224 Vangelis Koukis
    firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
661 244c552b Giorgos Verigakis
                                        max_length=30, null=True)
662 0196d9a3 Christos Stavrakakis
    dirty = models.BooleanField(default=False)
663 ce55f211 Kostas Papadimitriou
664 2987cd8a Giorgos Verigakis
    def __unicode__(self):
665 2987cd8a Giorgos Verigakis
        return '%s@%s' % (self.machine.name, self.network.name)
666 f533f224 Vangelis Koukis
667 f533f224 Vangelis Koukis
668 03992c72 Christos Stavrakakis
class PoolTable(models.Model):
669 03992c72 Christos Stavrakakis
    available_map = models.TextField(default="", null=False)
670 03992c72 Christos Stavrakakis
    reserved_map = models.TextField(default="", null=False)
671 03992c72 Christos Stavrakakis
    size = models.IntegerField(null=False)
672 e1334eb6 Christos Stavrakakis
673 03992c72 Christos Stavrakakis
    # Optional Fields
674 03992c72 Christos Stavrakakis
    base = models.CharField(null=True, max_length=32)
675 03992c72 Christos Stavrakakis
    offset = models.IntegerField(null=True)
676 ce55f211 Kostas Papadimitriou
677 e1334eb6 Christos Stavrakakis
    objects = ForUpdateManager()
678 e1334eb6 Christos Stavrakakis
679 e6a42a96 Christos Stavrakakis
    class Meta:
680 e6a42a96 Christos Stavrakakis
        abstract = True
681 e6a42a96 Christos Stavrakakis
682 e6a42a96 Christos Stavrakakis
    @classmethod
683 03992c72 Christos Stavrakakis
    def get_pool(cls):
684 e6a42a96 Christos Stavrakakis
        try:
685 21c9042c Christos Stavrakakis
            pool_row = cls.objects.select_for_update().get()
686 03992c72 Christos Stavrakakis
            return pool_row.pool
687 e6a42a96 Christos Stavrakakis
        except IndexError:
688 03992c72 Christos Stavrakakis
            raise pools.EmptyPool
689 e6a42a96 Christos Stavrakakis
690 03992c72 Christos Stavrakakis
    @property
691 03992c72 Christos Stavrakakis
    def pool(self):
692 03992c72 Christos Stavrakakis
        return self.manager(self)
693 e6a42a96 Christos Stavrakakis
694 e6a42a96 Christos Stavrakakis
695 03992c72 Christos Stavrakakis
class BridgePoolTable(PoolTable):
696 03992c72 Christos Stavrakakis
    manager = pools.BridgePool
697 e6a42a96 Christos Stavrakakis
698 fdc94944 Christos Stavrakakis
699 03992c72 Christos Stavrakakis
class MacPrefixPoolTable(PoolTable):
700 03992c72 Christos Stavrakakis
    manager = pools.MacPrefixPool
701 fdc94944 Christos Stavrakakis
702 fdc94944 Christos Stavrakakis
703 fdc94944 Christos Stavrakakis
class IPPoolTable(PoolTable):
704 fdc94944 Christos Stavrakakis
    manager = pools.IPPool
705 3524241a Christos Stavrakakis
706 3524241a Christos Stavrakakis
707 3524241a Christos Stavrakakis
@contextmanager
708 3524241a Christos Stavrakakis
def pooled_rapi_client(obj):
709 3524241a Christos Stavrakakis
        if isinstance(obj, VirtualMachine):
710 3524241a Christos Stavrakakis
            backend = obj.backend
711 3524241a Christos Stavrakakis
        else:
712 3524241a Christos Stavrakakis
            backend = obj
713 3524241a Christos Stavrakakis
714 3524241a Christos Stavrakakis
        if backend.offline:
715 3524241a Christos Stavrakakis
            raise ServiceUnavailable
716 3524241a Christos Stavrakakis
717 3524241a Christos Stavrakakis
        b = backend
718 3524241a Christos Stavrakakis
        client = get_rapi_client(b.id, b.hash, b.clustername, b.port,
719 3524241a Christos Stavrakakis
                                 b.username, b.password)
720 4216cd83 Christos Stavrakakis
        try:
721 4216cd83 Christos Stavrakakis
            yield client
722 4216cd83 Christos Stavrakakis
        finally:
723 4216cd83 Christos Stavrakakis
            put_rapi_client(client)
724 6ce1fc56 Kostas Papadimitriou
725 6ce1fc56 Kostas Papadimitriou
726 6ce1fc56 Kostas Papadimitriou
class VirtualMachineDiagnosticManager(models.Manager):
727 6ce1fc56 Kostas Papadimitriou
    """
728 6ce1fc56 Kostas Papadimitriou
    Custom manager for :class:`VirtualMachineDiagnostic` model.
729 6ce1fc56 Kostas Papadimitriou
    """
730 6ce1fc56 Kostas Papadimitriou
731 6ce1fc56 Kostas Papadimitriou
    # diagnostic creation helpers
732 6ce1fc56 Kostas Papadimitriou
    def create_for_vm(self, vm, level, message, **kwargs):
733 6ce1fc56 Kostas Papadimitriou
        attrs = {'machine': vm, 'level': level, 'message': message}
734 6ce1fc56 Kostas Papadimitriou
        attrs.update(kwargs)
735 6ce1fc56 Kostas Papadimitriou
        # update instance updated time
736 6ce1fc56 Kostas Papadimitriou
        self.create(**attrs)
737 6ce1fc56 Kostas Papadimitriou
        vm.save()
738 6ce1fc56 Kostas Papadimitriou
739 6ce1fc56 Kostas Papadimitriou
    def create_error(self, vm, **kwargs):
740 6ce1fc56 Kostas Papadimitriou
        self.create_for_vm(vm, 'ERROR', **kwargs)
741 6ce1fc56 Kostas Papadimitriou
742 6ce1fc56 Kostas Papadimitriou
    def create_debug(self, vm, **kwargs):
743 6ce1fc56 Kostas Papadimitriou
        self.create_for_vm(vm, 'DEBUG', **kwargs)
744 6ce1fc56 Kostas Papadimitriou
745 6ce1fc56 Kostas Papadimitriou
    def since(self, vm, created_since, **kwargs):
746 6ce1fc56 Kostas Papadimitriou
        return self.get_query_set().filter(vm=vm, created__gt=created_since,
747 6ce1fc56 Kostas Papadimitriou
                **kwargs)
748 6ce1fc56 Kostas Papadimitriou
749 6ce1fc56 Kostas Papadimitriou
750 6ce1fc56 Kostas Papadimitriou
class VirtualMachineDiagnostic(models.Model):
751 6ce1fc56 Kostas Papadimitriou
    """
752 6ce1fc56 Kostas Papadimitriou
    Model to store backend information messages that relate to the state of
753 6ce1fc56 Kostas Papadimitriou
    the virtual machine.
754 6ce1fc56 Kostas Papadimitriou
    """
755 6ce1fc56 Kostas Papadimitriou
756 6ce1fc56 Kostas Papadimitriou
    TYPES = (
757 6ce1fc56 Kostas Papadimitriou
        ('ERROR', 'Error'),
758 6ce1fc56 Kostas Papadimitriou
        ('WARNING', 'Warning'),
759 6ce1fc56 Kostas Papadimitriou
        ('INFO', 'Info'),
760 6ce1fc56 Kostas Papadimitriou
        ('DEBUG', 'Debug'),
761 6ce1fc56 Kostas Papadimitriou
    )
762 6ce1fc56 Kostas Papadimitriou
763 6ce1fc56 Kostas Papadimitriou
    objects = VirtualMachineDiagnosticManager()
764 6ce1fc56 Kostas Papadimitriou
765 6ce1fc56 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True)
766 6ce1fc56 Kostas Papadimitriou
    machine = models.ForeignKey('VirtualMachine', related_name="diagnostics")
767 6ce1fc56 Kostas Papadimitriou
    level = models.CharField(max_length=20, choices=TYPES)
768 6ce1fc56 Kostas Papadimitriou
    source = models.CharField(max_length=100)
769 6ce1fc56 Kostas Papadimitriou
    source_date = models.DateTimeField(null=True)
770 6ce1fc56 Kostas Papadimitriou
    message = models.CharField(max_length=255)
771 6ce1fc56 Kostas Papadimitriou
    details = models.TextField(null=True)
772 6ce1fc56 Kostas Papadimitriou
773 6ce1fc56 Kostas Papadimitriou
    class Meta:
774 6ce1fc56 Kostas Papadimitriou
        ordering = ['-created']