Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (28.4 kB)

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

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