Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (29.6 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 bd87213f Christos Stavrakakis
from copy import deepcopy
33 9dbe70f8 Markos Gogoulos
from django.conf import settings
34 17935f41 Vassilios Karakoidas
from django.db import models
35 aed9b901 Christos Stavrakakis
from django.db import IntegrityError
36 3165f027 Christos Stavrakakis
37 3165f027 Christos Stavrakakis
import utils
38 3524241a Christos Stavrakakis
from contextlib import contextmanager
39 aed9b901 Christos Stavrakakis
from hashlib import sha1
40 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
41 e6a42a96 Christos Stavrakakis
from synnefo import settings as snf_settings
42 30e0ed74 Christos Stavrakakis
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield
43 aed9b901 Christos Stavrakakis
44 864bed43 Christos Stavrakakis
from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager
45 03992c72 Christos Stavrakakis
from synnefo.db import pools
46 864bed43 Christos Stavrakakis
47 3524241a Christos Stavrakakis
from synnefo.logic.rapi_pool import (get_rapi_client,
48 3524241a Christos Stavrakakis
                                     put_rapi_client)
49 aed9b901 Christos Stavrakakis
50 b533e9b2 Christos Stavrakakis
import logging
51 b533e9b2 Christos Stavrakakis
log = logging.getLogger(__name__)
52 b533e9b2 Christos Stavrakakis
53 aed9b901 Christos Stavrakakis
54 e7164ebc Vassilios Karakoidas
class Flavor(models.Model):
55 13b3c5ff Vassilios Karakoidas
    cpu = models.IntegerField('Number of CPUs', default=0)
56 c909cbbd Giorgos Verigakis
    ram = models.IntegerField('RAM size in MiB', default=0)
57 c909cbbd Giorgos Verigakis
    disk = models.IntegerField('Disk size in GiB', default=0)
58 c909cbbd Giorgos Verigakis
    disk_template = models.CharField('Disk template', max_length=32,
59 68b952f9 Christos Stavrakakis
                       default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
60 2c60cbef Giorgos Verigakis
    deleted = models.BooleanField('Deleted', default=False)
61 ce55f211 Kostas Papadimitriou
62 be7b8d37 Vassilios Karakoidas
    class Meta:
63 dcfc6c2d Vangelis Koukis
        verbose_name = u'Virtual machine flavor'
64 b1af40e2 Giorgos Verigakis
        unique_together = ('cpu', 'ram', 'disk', 'disk_template')
65 ce55f211 Kostas Papadimitriou
66 9ff7ca6f Giorgos Verigakis
    @property
67 9ff7ca6f Giorgos Verigakis
    def name(self):
68 f0f4edac Vassilios Karakoidas
        """Returns flavor name (generated)"""
69 b65a3b04 Christos Stavrakakis
        return u'C%dR%dD%d%s' % (self.cpu, self.ram, self.disk,
70 b65a3b04 Christos Stavrakakis
                                 self.disk_template)
71 ce55f211 Kostas Papadimitriou
72 46547e14 Faidon Liambotis
    def __unicode__(self):
73 cfd70896 Christos Stavrakakis
        return str(self.id)
74 452d2391 Vassilios Karakoidas
75 46547e14 Faidon Liambotis
76 aed9b901 Christos Stavrakakis
class Backend(models.Model):
77 aed9b901 Christos Stavrakakis
    clustername = models.CharField('Cluster Name', max_length=128, unique=True)
78 aed9b901 Christos Stavrakakis
    port = models.PositiveIntegerField('Port', default=5080)
79 aed9b901 Christos Stavrakakis
    username = models.CharField('Username', max_length=64, blank=True,
80 aed9b901 Christos Stavrakakis
                                null=True)
81 2a8a60d5 Christos Stavrakakis
    password_hash = models.CharField('Password', max_length=128, blank=True,
82 68b952f9 Christos Stavrakakis
                                     null=True)
83 aed9b901 Christos Stavrakakis
    # Sha1 is up to 40 characters long
84 aed9b901 Christos Stavrakakis
    hash = models.CharField('Hash', max_length=40, editable=False, null=False)
85 3165f027 Christos Stavrakakis
    # Unique index of the Backend, used for the mac-prefixes of the
86 3165f027 Christos Stavrakakis
    # BackendNetworks
87 3165f027 Christos Stavrakakis
    index = models.PositiveIntegerField('Index', null=False, unique=True,
88 3165f027 Christos Stavrakakis
                                        default=0)
89 aed9b901 Christos Stavrakakis
    drained = models.BooleanField('Drained', default=False, null=False)
90 aed9b901 Christos Stavrakakis
    offline = models.BooleanField('Offline', default=False, null=False)
91 bd87213f Christos Stavrakakis
    # Type of hypervisor
92 bd87213f Christos Stavrakakis
    hypervisor = models.CharField('Hypervisor', max_length=32, default="kvm",
93 bd87213f Christos Stavrakakis
                                  null=False)
94 aed9b901 Christos Stavrakakis
    # Last refresh of backend resources
95 aed9b901 Christos Stavrakakis
    updated = models.DateTimeField(auto_now_add=True)
96 aed9b901 Christos Stavrakakis
    # Backend resources
97 aed9b901 Christos Stavrakakis
    mfree = models.PositiveIntegerField('Free Memory', default=0, null=False)
98 aed9b901 Christos Stavrakakis
    mtotal = models.PositiveIntegerField('Total Memory', default=0, null=False)
99 aed9b901 Christos Stavrakakis
    dfree = models.PositiveIntegerField('Free Disk', default=0, null=False)
100 aed9b901 Christos Stavrakakis
    dtotal = models.PositiveIntegerField('Total Disk', default=0, null=False)
101 aed9b901 Christos Stavrakakis
    pinst_cnt = models.PositiveIntegerField('Primary Instances', default=0,
102 aed9b901 Christos Stavrakakis
                                            null=False)
103 aed9b901 Christos Stavrakakis
    ctotal = models.PositiveIntegerField('Total number of logical processors',
104 aed9b901 Christos Stavrakakis
                                         default=0, null=False)
105 aed9b901 Christos Stavrakakis
    # Custom object manager to protect from cascade delete
106 864bed43 Christos Stavrakakis
    objects = ProtectedDeleteManager()
107 aed9b901 Christos Stavrakakis
108 4c9ac139 Christos Stavrakakis
    HYPERVISORS = (
109 4c9ac139 Christos Stavrakakis
        ("kvm", "Linux KVM hypervisor"),
110 4c9ac139 Christos Stavrakakis
        ("xen-pvm", "Xen PVM hypervisor"),
111 4c9ac139 Christos Stavrakakis
        ("xen-hvm", "Xen KVM hypervisor"),
112 4c9ac139 Christos Stavrakakis
    )
113 4c9ac139 Christos Stavrakakis
114 aed9b901 Christos Stavrakakis
    class Meta:
115 aed9b901 Christos Stavrakakis
        verbose_name = u'Backend'
116 aed9b901 Christos Stavrakakis
        ordering = ["clustername"]
117 aed9b901 Christos Stavrakakis
118 aed9b901 Christos Stavrakakis
    def __unicode__(self):
119 bf5c82dc Christos Stavrakakis
        return self.clustername + "(id=" + str(self.id) + ")"
120 aed9b901 Christos Stavrakakis
121 aed9b901 Christos Stavrakakis
    @property
122 aed9b901 Christos Stavrakakis
    def backend_id(self):
123 aed9b901 Christos Stavrakakis
        return self.id
124 aed9b901 Christos Stavrakakis
125 3524241a Christos Stavrakakis
    def get_client(self):
126 aed9b901 Christos Stavrakakis
        """Get or create a client. """
127 3524241a Christos Stavrakakis
        if self.offline:
128 bd40abfa Christos Stavrakakis
            raise faults.ServiceUnavailable
129 3524241a Christos Stavrakakis
        return get_rapi_client(self.id, self.hash,
130 cc50e51a Christos Stavrakakis
                               self.clustername,
131 cc50e51a Christos Stavrakakis
                               self.port,
132 3524241a Christos Stavrakakis
                               self.username,
133 3524241a Christos Stavrakakis
                               self.password)
134 3524241a Christos Stavrakakis
135 3524241a Christos Stavrakakis
    @staticmethod
136 3524241a Christos Stavrakakis
    def put_client(client):
137 3524241a Christos Stavrakakis
            put_rapi_client(client)
138 aed9b901 Christos Stavrakakis
139 aed9b901 Christos Stavrakakis
    def create_hash(self):
140 aed9b901 Christos Stavrakakis
        """Create a hash for this backend. """
141 68b952f9 Christos Stavrakakis
        sha = sha1('%s%s%s%s' %
142 68b952f9 Christos Stavrakakis
                   (self.clustername, self.port, self.username, self.password))
143 68b952f9 Christos Stavrakakis
        return sha.hexdigest()
144 aed9b901 Christos Stavrakakis
145 30e0ed74 Christos Stavrakakis
    @property
146 30e0ed74 Christos Stavrakakis
    def password(self):
147 30e0ed74 Christos Stavrakakis
        return decrypt_db_charfield(self.password_hash)
148 30e0ed74 Christos Stavrakakis
149 30e0ed74 Christos Stavrakakis
    @password.setter
150 30e0ed74 Christos Stavrakakis
    def password(self, value):
151 30e0ed74 Christos Stavrakakis
        self.password_hash = encrypt_db_charfield(value)
152 30e0ed74 Christos Stavrakakis
153 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
154 aed9b901 Christos Stavrakakis
        # Create a new hash each time a Backend is saved
155 aed9b901 Christos Stavrakakis
        old_hash = self.hash
156 aed9b901 Christos Stavrakakis
        self.hash = self.create_hash()
157 aed9b901 Christos Stavrakakis
        super(Backend, self).save(*args, **kwargs)
158 aed9b901 Christos Stavrakakis
        if self.hash != old_hash:
159 aed9b901 Christos Stavrakakis
            # Populate the new hash to the new instances
160 68b952f9 Christos Stavrakakis
            self.virtual_machines.filter(deleted=False)\
161 68b952f9 Christos Stavrakakis
                                 .update(backend_hash=self.hash)
162 aed9b901 Christos Stavrakakis
163 aed9b901 Christos Stavrakakis
    def delete(self, *args, **kwargs):
164 aed9b901 Christos Stavrakakis
        # Integrity Error if non-deleted VMs are associated with Backend
165 aed9b901 Christos Stavrakakis
        if self.virtual_machines.filter(deleted=False).count():
166 aed9b901 Christos Stavrakakis
            raise IntegrityError("Non-deleted virtual machines are associated "
167 aed9b901 Christos Stavrakakis
                                 "with backend: %s" % self)
168 aed9b901 Christos Stavrakakis
        else:
169 aed9b901 Christos Stavrakakis
            # ON_DELETE = SET NULL
170 ce9abd26 Christos Stavrakakis
            for vm in self.virtual_machines.all():
171 ce9abd26 Christos Stavrakakis
                vm.backend = None
172 ce9abd26 Christos Stavrakakis
                vm.save()
173 fd65ab41 Christos Stavrakakis
            self.virtual_machines.all().backend = None
174 82c1c610 Christos Stavrakakis
            # Remove BackendNetworks of this Backend.
175 82c1c610 Christos Stavrakakis
            # Do not use networks.all().delete(), since delete() method of
176 82c1c610 Christos Stavrakakis
            # BackendNetwork will not be called!
177 82c1c610 Christos Stavrakakis
            for net in self.networks.all():
178 82c1c610 Christos Stavrakakis
                net.delete()
179 aed9b901 Christos Stavrakakis
            super(Backend, self).delete(*args, **kwargs)
180 aed9b901 Christos Stavrakakis
181 3165f027 Christos Stavrakakis
    def __init__(self, *args, **kwargs):
182 3165f027 Christos Stavrakakis
        super(Backend, self).__init__(*args, **kwargs)
183 3165f027 Christos Stavrakakis
        if not self.pk:
184 3165f027 Christos Stavrakakis
            # Generate a unique index for the Backend
185 3165f027 Christos Stavrakakis
            indexes = Backend.objects.all().values_list('index', flat=True)
186 ce9abd26 Christos Stavrakakis
            try:
187 ce9abd26 Christos Stavrakakis
                first_free = [x for x in xrange(0, 16) if x not in indexes][0]
188 ce9abd26 Christos Stavrakakis
                self.index = first_free
189 ce9abd26 Christos Stavrakakis
            except IndexError:
190 ce9abd26 Christos Stavrakakis
                raise Exception("Can not create more than 16 backends")
191 3165f027 Christos Stavrakakis
192 bd87213f Christos Stavrakakis
    def use_hotplug(self):
193 bd87213f Christos Stavrakakis
        return self.hypervisor == "kvm" and snf_settings.GANETI_USE_HOTPLUG
194 bd87213f Christos Stavrakakis
195 bd87213f Christos Stavrakakis
    def get_create_params(self):
196 bd87213f Christos Stavrakakis
        params = deepcopy(snf_settings.GANETI_CREATEINSTANCE_KWARGS)
197 bd87213f Christos Stavrakakis
        params["hvparams"] = params.get("hvparams", {})\
198 bd87213f Christos Stavrakakis
                                   .get(self.hypervisor, {})
199 bd87213f Christos Stavrakakis
        return params
200 bd87213f Christos Stavrakakis
201 aed9b901 Christos Stavrakakis
202 fd65ab41 Christos Stavrakakis
# A backend job may be in one of the following possible states
203 fd65ab41 Christos Stavrakakis
BACKEND_STATUSES = (
204 fd65ab41 Christos Stavrakakis
    ('queued', 'request queued'),
205 fd65ab41 Christos Stavrakakis
    ('waiting', 'request waiting for locks'),
206 fd65ab41 Christos Stavrakakis
    ('canceling', 'request being canceled'),
207 fd65ab41 Christos Stavrakakis
    ('running', 'request running'),
208 fd65ab41 Christos Stavrakakis
    ('canceled', 'request canceled'),
209 fd65ab41 Christos Stavrakakis
    ('success', 'request completed successfully'),
210 fd65ab41 Christos Stavrakakis
    ('error', 'request returned error')
211 fd65ab41 Christos Stavrakakis
)
212 fd65ab41 Christos Stavrakakis
213 fd65ab41 Christos Stavrakakis
214 3416e629 Christos Stavrakakis
class QuotaHolderSerial(models.Model):
215 97fffe10 Christos Stavrakakis
    """Model representing a serial for a Quotaholder Commission.
216 97fffe10 Christos Stavrakakis

217 97fffe10 Christos Stavrakakis
    serial:   The serial that Quotaholder assigned to this commission
218 97fffe10 Christos Stavrakakis
    pending:  Whether it has been decided to accept or reject this commission
219 97fffe10 Christos Stavrakakis
    accept:   If pending is False, this attribute indicates whether to accept
220 97fffe10 Christos Stavrakakis
              or reject this commission
221 97fffe10 Christos Stavrakakis
    resolved: Whether this commission has been accepted or rejected to
222 97fffe10 Christos Stavrakakis
              Quotaholder.
223 97fffe10 Christos Stavrakakis

224 97fffe10 Christos Stavrakakis
    """
225 68b952f9 Christos Stavrakakis
    serial = models.BigIntegerField(null=False, primary_key=True,
226 68b952f9 Christos Stavrakakis
                                    db_index=True)
227 3416e629 Christos Stavrakakis
    pending = models.BooleanField(default=True, db_index=True)
228 97fffe10 Christos Stavrakakis
    accept = models.BooleanField(default=False)
229 97fffe10 Christos Stavrakakis
    resolved = models.BooleanField(default=False)
230 3416e629 Christos Stavrakakis
231 3416e629 Christos Stavrakakis
    class Meta:
232 3416e629 Christos Stavrakakis
        verbose_name = u'Quota Serial'
233 3416e629 Christos Stavrakakis
        ordering = ["serial"]
234 3416e629 Christos Stavrakakis
235 3416e629 Christos Stavrakakis
236 07f3219d Vassilios Karakoidas
class VirtualMachine(models.Model):
237 c92af313 Vangelis Koukis
    # The list of possible actions for a VM
238 d08a5f6f Vangelis Koukis
    ACTIONS = (
239 68b952f9 Christos Stavrakakis
        ('CREATE', 'Create VM'),
240 68b952f9 Christos Stavrakakis
        ('START', 'Start VM'),
241 68b952f9 Christos Stavrakakis
        ('STOP', 'Shutdown VM'),
242 68b952f9 Christos Stavrakakis
        ('SUSPEND', 'Admin Suspend VM'),
243 68b952f9 Christos Stavrakakis
        ('REBOOT', 'Reboot VM'),
244 68b952f9 Christos Stavrakakis
        ('DESTROY', 'Destroy VM')
245 d08a5f6f Vangelis Koukis
    )
246 ce55f211 Kostas Papadimitriou
247 c92af313 Vangelis Koukis
    # The internal operating state of a VM
248 d08a5f6f Vangelis Koukis
    OPER_STATES = (
249 d08a5f6f Vangelis Koukis
        ('BUILD', 'Queued for creation'),
250 d08a5f6f Vangelis Koukis
        ('ERROR', 'Creation failed'),
251 d08a5f6f Vangelis Koukis
        ('STOPPED', 'Stopped'),
252 d08a5f6f Vangelis Koukis
        ('STARTED', 'Started'),
253 d08a5f6f Vangelis Koukis
        ('DESTROYED', 'Destroyed')
254 d08a5f6f Vangelis Koukis
    )
255 ce55f211 Kostas Papadimitriou
256 c92af313 Vangelis Koukis
    # The list of possible operations on the backend
257 d08a5f6f Vangelis Koukis
    BACKEND_OPCODES = (
258 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_CREATE', 'Create Instance'),
259 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_REMOVE', 'Remove Instance'),
260 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_STARTUP', 'Startup Instance'),
261 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_SHUTDOWN', 'Shutdown Instance'),
262 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_REBOOT', 'Reboot Instance'),
263 26fef2b9 Vangelis Koukis
264 26fef2b9 Vangelis Koukis
        # These are listed here for completeness,
265 26fef2b9 Vangelis Koukis
        # and are ignored for the time being
266 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_SET_PARAMS', 'Set Instance Parameters'),
267 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_QUERY_DATA', 'Query Instance Data'),
268 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REINSTALL', 'Reinstall Instance'),
269 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_ACTIVATE_DISKS', 'Activate Disks'),
270 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_DEACTIVATE_DISKS', 'Deactivate Disks'),
271 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REPLACE_DISKS', 'Replace Disks'),
272 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_MIGRATE', 'Migrate Instance'),
273 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_CONSOLE', 'Get Instance Console'),
274 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_RECREATE_DISKS', 'Recreate Disks'),
275 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_FAILOVER', 'Failover Instance')
276 d08a5f6f Vangelis Koukis
    )
277 ce55f211 Kostas Papadimitriou
278 d08a5f6f Vangelis Koukis
    # The operating state of a VM,
279 d08a5f6f Vangelis Koukis
    # upon the successful completion of a backend operation.
280 21d8adbf Vangelis Koukis
    # IMPORTANT: Make sure all keys have a corresponding
281 21d8adbf Vangelis Koukis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
282 d08a5f6f Vangelis Koukis
    OPER_STATE_FROM_OPCODE = {
283 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_CREATE': 'STARTED',
284 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_REMOVE': 'DESTROYED',
285 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_STARTUP': 'STARTED',
286 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_SHUTDOWN': 'STOPPED',
287 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_REBOOT': 'STARTED',
288 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_SET_PARAMS': None,
289 8490bf84 Georgios Gousios
        'OP_INSTANCE_QUERY_DATA': None,
290 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REINSTALL': None,
291 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_ACTIVATE_DISKS': None,
292 8490bf84 Georgios Gousios
        'OP_INSTANCE_DEACTIVATE_DISKS': None,
293 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REPLACE_DISKS': None,
294 8490bf84 Georgios Gousios
        'OP_INSTANCE_MIGRATE': None,
295 8490bf84 Georgios Gousios
        'OP_INSTANCE_CONSOLE': None,
296 41303ed0 Vangelis Koukis
        'OP_INSTANCE_RECREATE_DISKS': None,
297 41303ed0 Vangelis Koukis
        'OP_INSTANCE_FAILOVER': None
298 d08a5f6f Vangelis Koukis
    }
299 d08a5f6f Vangelis Koukis
300 c92af313 Vangelis Koukis
    # This dictionary contains the correspondence between
301 c92af313 Vangelis Koukis
    # internal operating states and Server States as defined
302 c92af313 Vangelis Koukis
    # by the Rackspace API.
303 d08a5f6f Vangelis Koukis
    RSAPI_STATE_FROM_OPER_STATE = {
304 d08a5f6f Vangelis Koukis
        "BUILD": "BUILD",
305 d08a5f6f Vangelis Koukis
        "ERROR": "ERROR",
306 d08a5f6f Vangelis Koukis
        "STOPPED": "STOPPED",
307 d08a5f6f Vangelis Koukis
        "STARTED": "ACTIVE",
308 d08a5f6f Vangelis Koukis
        "DESTROYED": "DELETED"
309 d08a5f6f Vangelis Koukis
    }
310 d08a5f6f Vangelis Koukis
311 4daac449 Vassilios Karakoidas
    name = models.CharField('Virtual Machine Name', max_length=255)
312 190d155f Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=100,
313 e18c1749 Christos Stavrakakis
                              db_index=True, null=False)
314 aed9b901 Christos Stavrakakis
    backend = models.ForeignKey(Backend, null=True,
315 aed9b901 Christos Stavrakakis
                                related_name="virtual_machines",)
316 aed9b901 Christos Stavrakakis
    backend_hash = models.CharField(max_length=128, null=True, editable=False)
317 d174b105 Vassilios Karakoidas
    created = models.DateTimeField(auto_now_add=True)
318 d174b105 Vassilios Karakoidas
    updated = models.DateTimeField(auto_now=True)
319 936afb7b Giorgos Verigakis
    imageid = models.CharField(max_length=100, null=False)
320 22571df6 Vassilios Karakoidas
    hostid = models.CharField(max_length=100)
321 46547e14 Faidon Liambotis
    flavor = models.ForeignKey(Flavor)
322 190d155f Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
323 9ff7ca6f Giorgos Verigakis
    suspended = models.BooleanField('Administratively Suspended',
324 244c552b Giorgos Verigakis
                                    default=False)
325 004967e4 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial,
326 68b952f9 Christos Stavrakakis
                               related_name='virtual_machine', null=True)
327 d08a5f6f Vangelis Koukis
328 ce55f211 Kostas Papadimitriou
    # VM State
329 52194743 Vangelis Koukis
    # The following fields are volatile data, in the sense
330 52194743 Vangelis Koukis
    # that they need not be persistent in the DB, but rather
331 52194743 Vangelis Koukis
    # get generated at runtime by quering Ganeti and applying
332 52194743 Vangelis Koukis
    # updates received from Ganeti.
333 ce55f211 Kostas Papadimitriou
334 73b2b65e Vassilios Karakoidas
    # In the future they could be moved to a separate caching layer
335 73b2b65e Vassilios Karakoidas
    # and removed from the database.
336 73b2b65e Vassilios Karakoidas
    # [vkoukis] after discussion with [faidon].
337 dfd19c2d Vassilios Karakoidas
    action = models.CharField(choices=ACTIONS, max_length=30, null=True)
338 dfd19c2d Vassilios Karakoidas
    operstate = models.CharField(choices=OPER_STATES, max_length=30, null=True)
339 dfd19c2d Vassilios Karakoidas
    backendjobid = models.PositiveIntegerField(null=True)
340 f533f224 Vangelis Koukis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
341 aed9b901 Christos Stavrakakis
                                     null=True)
342 9ff7ca6f Giorgos Verigakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
343 aed9b901 Christos Stavrakakis
                                        max_length=30, null=True)
344 dfd19c2d Vassilios Karakoidas
    backendlogmsg = models.TextField(null=True)
345 19da4325 Vangelis Koukis
    buildpercentage = models.IntegerField(default=0)
346 c4e55622 Christos Stavrakakis
    backendtime = models.DateTimeField(default=datetime.datetime.min)
347 9068cd85 Georgios Gousios
348 7f2dbcad Christos Stavrakakis
    objects = ForUpdateManager()
349 7f2dbcad Christos Stavrakakis
350 3524241a Christos Stavrakakis
    def get_client(self):
351 3524241a Christos Stavrakakis
        if self.backend:
352 2cea7f38 Christos Stavrakakis
            return self.backend.get_client()
353 aed9b901 Christos Stavrakakis
        else:
354 bd40abfa Christos Stavrakakis
            raise faults.ServiceUnavailable
355 aed9b901 Christos Stavrakakis
356 6ce1fc56 Kostas Papadimitriou
    def get_last_diagnostic(self, **filters):
357 6ce1fc56 Kostas Papadimitriou
        try:
358 6ce1fc56 Kostas Papadimitriou
            return self.diagnostics.filter()[0]
359 6ce1fc56 Kostas Papadimitriou
        except IndexError:
360 6ce1fc56 Kostas Papadimitriou
            return None
361 6ce1fc56 Kostas Papadimitriou
362 3524241a Christos Stavrakakis
    @staticmethod
363 3524241a Christos Stavrakakis
    def put_client(client):
364 3524241a Christos Stavrakakis
            put_rapi_client(client)
365 3524241a Christos Stavrakakis
366 3846dfd0 Vassilios Karakoidas
    def __init__(self, *args, **kw):
367 d08a5f6f Vangelis Koukis
        """Initialize state for just created VM instances."""
368 d08a5f6f Vangelis Koukis
        super(VirtualMachine, self).__init__(*args, **kw)
369 bc9c872b Vangelis Koukis
        # This gets called BEFORE an instance gets save()d for
370 bc9c872b Vangelis Koukis
        # the first time.
371 ce55f211 Kostas Papadimitriou
        if not self.pk:
372 dfd19c2d Vassilios Karakoidas
            self.action = None
373 dfd19c2d Vassilios Karakoidas
            self.backendjobid = None
374 dfd19c2d Vassilios Karakoidas
            self.backendjobstatus = None
375 dfd19c2d Vassilios Karakoidas
            self.backendopcode = None
376 dfd19c2d Vassilios Karakoidas
            self.backendlogmsg = None
377 dfd19c2d Vassilios Karakoidas
            self.operstate = 'BUILD'
378 ce55f211 Kostas Papadimitriou
379 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
380 aed9b901 Christos Stavrakakis
        # Store hash for first time saved vm
381 aed9b901 Christos Stavrakakis
        if (self.id is None or self.backend_hash == '') and self.backend:
382 aed9b901 Christos Stavrakakis
            self.backend_hash = self.backend.hash
383 aed9b901 Christos Stavrakakis
        super(VirtualMachine, self).save(*args, **kwargs)
384 aed9b901 Christos Stavrakakis
385 9ff7ca6f Giorgos Verigakis
    @property
386 924d8085 Christos Stavrakakis
    def backend_vm_id(self):
387 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
388 76ba77c1 Vangelis Koukis
        if not self.id:
389 76ba77c1 Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError("self.id is None")
390 d30f29aa Christos Stavrakakis
        return "%s%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
391 ce55f211 Kostas Papadimitriou
392 d08a5f6f Vangelis Koukis
    class Meta:
393 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
394 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
395 ce55f211 Kostas Papadimitriou
396 d08a5f6f Vangelis Koukis
    def __unicode__(self):
397 cfd70896 Christos Stavrakakis
        return str(self.id)
398 d08a5f6f Vangelis Koukis
399 048fab1e Christos Stavrakakis
    # Error classes
400 048fab1e Christos Stavrakakis
    class InvalidBackendIdError(Exception):
401 048fab1e Christos Stavrakakis
        def __init__(self, value):
402 048fab1e Christos Stavrakakis
            self.value = value
403 048fab1e Christos Stavrakakis
404 048fab1e Christos Stavrakakis
        def __str__(self):
405 048fab1e Christos Stavrakakis
            return repr(self.value)
406 048fab1e Christos Stavrakakis
407 048fab1e Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
408 048fab1e Christos Stavrakakis
        def __init__(self, opcode, status):
409 048fab1e Christos Stavrakakis
            self.opcode = opcode
410 048fab1e Christos Stavrakakis
            self.status = status
411 048fab1e Christos Stavrakakis
412 048fab1e Christos Stavrakakis
        def __str__(self):
413 048fab1e Christos Stavrakakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
414 048fab1e Christos Stavrakakis
                        self.status))
415 048fab1e Christos Stavrakakis
416 048fab1e Christos Stavrakakis
    class InvalidActionError(Exception):
417 048fab1e Christos Stavrakakis
        def __init__(self, action):
418 048fab1e Christos Stavrakakis
            self._action = action
419 048fab1e Christos Stavrakakis
420 048fab1e Christos Stavrakakis
        def __str__(self):
421 048fab1e Christos Stavrakakis
            return repr(str(self._action))
422 048fab1e Christos Stavrakakis
423 4ffb82dc Vassilios Karakoidas
424 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
425 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
426 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
427 7cc3c7d9 Giorgos Verigakis
    vm = models.ForeignKey(VirtualMachine, related_name='metadata')
428 ce55f211 Kostas Papadimitriou
429 be7b8d37 Vassilios Karakoidas
    class Meta:
430 1bd24df6 Giorgos Verigakis
        unique_together = (('meta_key', 'vm'),)
431 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
432 ce55f211 Kostas Papadimitriou
433 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
434 9ff7ca6f Giorgos Verigakis
        return u'%s: %s' % (self.meta_key, self.meta_value)
435 be7b8d37 Vassilios Karakoidas
436 be7b8d37 Vassilios Karakoidas
437 0269afd6 Giorgos Verigakis
class Network(models.Model):
438 e6a42a96 Christos Stavrakakis
    OPER_STATES = (
439 99af08a4 Christos Stavrakakis
        ('PENDING', 'Pending'),  # Unused because of lazy networks
440 f533f224 Vangelis Koukis
        ('ACTIVE', 'Active'),
441 e6a42a96 Christos Stavrakakis
        ('DELETED', 'Deleted'),
442 e6a42a96 Christos Stavrakakis
        ('ERROR', 'Error')
443 e6a42a96 Christos Stavrakakis
    )
444 e6a42a96 Christos Stavrakakis
445 e6a42a96 Christos Stavrakakis
    ACTIONS = (
446 68b952f9 Christos Stavrakakis
        ('CREATE', 'Create Network'),
447 68b952f9 Christos Stavrakakis
        ('DESTROY', 'Destroy Network'),
448 e6a42a96 Christos Stavrakakis
    )
449 e6a42a96 Christos Stavrakakis
450 e6a42a96 Christos Stavrakakis
    RSAPI_STATE_FROM_OPER_STATE = {
451 e6a42a96 Christos Stavrakakis
        'PENDING': 'PENDING',
452 e6a42a96 Christos Stavrakakis
        'ACTIVE': 'ACTIVE',
453 e6a42a96 Christos Stavrakakis
        'DELETED': 'DELETED',
454 e6a42a96 Christos Stavrakakis
        'ERROR': 'ERROR'
455 e6a42a96 Christos Stavrakakis
    }
456 e6a42a96 Christos Stavrakakis
457 b7d38981 Dimitris Aragiorgis
    FLAVORS = {
458 b7d38981 Dimitris Aragiorgis
        'CUSTOM': {
459 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
460 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_BRIDGE,
461 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
462 68b952f9 Christos Stavrakakis
            'tags': None,
463 68b952f9 Christos Stavrakakis
            'desc': "Basic flavor used for a bridged network",
464 b7d38981 Dimitris Aragiorgis
        },
465 b7d38981 Dimitris Aragiorgis
        'IP_LESS_ROUTED': {
466 68b952f9 Christos Stavrakakis
            'mode': 'routed',
467 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_ROUTING_TABLE,
468 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
469 68b952f9 Christos Stavrakakis
            'tags': 'ip-less-routed',
470 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for an IP-less routed network using"
471 68b952f9 Christos Stavrakakis
                    " Proxy ARP",
472 b7d38981 Dimitris Aragiorgis
        },
473 b7d38981 Dimitris Aragiorgis
        'MAC_FILTERED': {
474 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
475 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
476 68b952f9 Christos Stavrakakis
            'mac_prefix': 'pool',
477 68b952f9 Christos Stavrakakis
            'tags': 'private-filtered',
478 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged networks that offer isolation"
479 68b952f9 Christos Stavrakakis
                    " via filtering packets based on their src "
480 68b952f9 Christos Stavrakakis
                    " MAC (ebtables)",
481 b7d38981 Dimitris Aragiorgis
        },
482 b7d38981 Dimitris Aragiorgis
        'PHYSICAL_VLAN': {
483 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
484 68b952f9 Christos Stavrakakis
            'link': 'pool',
485 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
486 68b952f9 Christos Stavrakakis
            'tags': 'physical-vlan',
487 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged network that offer isolation"
488 68b952f9 Christos Stavrakakis
                    " via dedicated physical vlan",
489 b7d38981 Dimitris Aragiorgis
        },
490 b7d38981 Dimitris Aragiorgis
    }
491 ce55f211 Kostas Papadimitriou
492 e6a42a96 Christos Stavrakakis
    name = models.CharField('Network Name', max_length=128)
493 190d155f Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=128,
494 190d155f Christos Stavrakakis
                              null=True, db_index=True)
495 e6a42a96 Christos Stavrakakis
    subnet = models.CharField('Subnet', max_length=32, default='10.0.0.0/24')
496 cc3f266e Christos Stavrakakis
    subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True)
497 e6a42a96 Christos Stavrakakis
    gateway = models.CharField('Gateway', max_length=32, null=True)
498 cc3f266e Christos Stavrakakis
    gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True)
499 e6a42a96 Christos Stavrakakis
    dhcp = models.BooleanField('DHCP', default=True)
500 b7d38981 Dimitris Aragiorgis
    flavor = models.CharField('Flavor', max_length=32, null=False)
501 b7d38981 Dimitris Aragiorgis
    mode = models.CharField('Network Mode', max_length=16, null=True)
502 b7d38981 Dimitris Aragiorgis
    link = models.CharField('Network Link', max_length=32, null=True)
503 3165f027 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
504 b7d38981 Dimitris Aragiorgis
    tags = models.CharField('Network Tags', max_length=128, null=True)
505 190d155f Christos Stavrakakis
    public = models.BooleanField(default=False, db_index=True)
506 0269afd6 Giorgos Verigakis
    created = models.DateTimeField(auto_now_add=True)
507 0269afd6 Giorgos Verigakis
    updated = models.DateTimeField(auto_now=True)
508 190d155f Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
509 fd65ab41 Christos Stavrakakis
    state = models.CharField(choices=OPER_STATES, max_length=32,
510 fd65ab41 Christos Stavrakakis
                             default='PENDING')
511 f533f224 Vangelis Koukis
    machines = models.ManyToManyField(VirtualMachine,
512 244c552b Giorgos Verigakis
                                      through='NetworkInterface')
513 fd65ab41 Christos Stavrakakis
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
514 fd65ab41 Christos Stavrakakis
                              default=None)
515 6dafedf6 Christos Stavrakakis
    drained = models.BooleanField("Drained", default=False, null=False)
516 e6a42a96 Christos Stavrakakis
517 fdc94944 Christos Stavrakakis
    pool = models.OneToOneField('IPPoolTable', related_name='network',
518 941992c4 Christos Stavrakakis
                default=lambda: IPPoolTable.objects.create(available_map='',
519 941992c4 Christos Stavrakakis
                                                           reserved_map='',
520 941992c4 Christos Stavrakakis
                                                           size=0),
521 941992c4 Christos Stavrakakis
                null=True)
522 004967e4 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
523 004967e4 Christos Stavrakakis
                               null=True)
524 77f0fa63 Christos Stavrakakis
525 864bed43 Christos Stavrakakis
    objects = ForUpdateManager()
526 77f0fa63 Christos Stavrakakis
527 cfd70896 Christos Stavrakakis
    def __unicode__(self):
528 cfd70896 Christos Stavrakakis
        return str(self.id)
529 cfd70896 Christos Stavrakakis
530 e6a42a96 Christos Stavrakakis
    @property
531 e6a42a96 Christos Stavrakakis
    def backend_id(self):
532 fd65ab41 Christos Stavrakakis
        """Return the backend id by prepending backend-prefix."""
533 e6a42a96 Christos Stavrakakis
        if not self.id:
534 e6a42a96 Christos Stavrakakis
            raise Network.InvalidBackendIdError("self.id is None")
535 d30f29aa Christos Stavrakakis
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
536 e6a42a96 Christos Stavrakakis
537 e6a42a96 Christos Stavrakakis
    @property
538 e6a42a96 Christos Stavrakakis
    def backend_tag(self):
539 e6a42a96 Christos Stavrakakis
        """Return the network tag to be used in backend
540 e6a42a96 Christos Stavrakakis

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