Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (29.3 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 c802789d Christos Stavrakakis
    disk_template = models.CharField('Disk template', max_length=32)
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 df36a125 Christos Stavrakakis
        return "<%s:%s>" % (str(self.id), self.name)
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 bd87213f Christos Stavrakakis
    # Type of hypervisor
91 bd87213f Christos Stavrakakis
    hypervisor = models.CharField('Hypervisor', max_length=32, default="kvm",
92 bd87213f Christos Stavrakakis
                                  null=False)
93 aed9b901 Christos Stavrakakis
    # Last refresh of backend resources
94 aed9b901 Christos Stavrakakis
    updated = models.DateTimeField(auto_now_add=True)
95 aed9b901 Christos Stavrakakis
    # Backend resources
96 aed9b901 Christos Stavrakakis
    mfree = models.PositiveIntegerField('Free Memory', default=0, null=False)
97 aed9b901 Christos Stavrakakis
    mtotal = models.PositiveIntegerField('Total Memory', default=0, null=False)
98 aed9b901 Christos Stavrakakis
    dfree = models.PositiveIntegerField('Free Disk', default=0, null=False)
99 aed9b901 Christos Stavrakakis
    dtotal = models.PositiveIntegerField('Total Disk', default=0, null=False)
100 aed9b901 Christos Stavrakakis
    pinst_cnt = models.PositiveIntegerField('Primary Instances', default=0,
101 aed9b901 Christos Stavrakakis
                                            null=False)
102 aed9b901 Christos Stavrakakis
    ctotal = models.PositiveIntegerField('Total number of logical processors',
103 aed9b901 Christos Stavrakakis
                                         default=0, null=False)
104 aed9b901 Christos Stavrakakis
    # Custom object manager to protect from cascade delete
105 864bed43 Christos Stavrakakis
    objects = ProtectedDeleteManager()
106 aed9b901 Christos Stavrakakis
107 4c9ac139 Christos Stavrakakis
    HYPERVISORS = (
108 4c9ac139 Christos Stavrakakis
        ("kvm", "Linux KVM hypervisor"),
109 4c9ac139 Christos Stavrakakis
        ("xen-pvm", "Xen PVM hypervisor"),
110 4c9ac139 Christos Stavrakakis
        ("xen-hvm", "Xen KVM hypervisor"),
111 4c9ac139 Christos Stavrakakis
    )
112 4c9ac139 Christos Stavrakakis
113 aed9b901 Christos Stavrakakis
    class Meta:
114 aed9b901 Christos Stavrakakis
        verbose_name = u'Backend'
115 aed9b901 Christos Stavrakakis
        ordering = ["clustername"]
116 aed9b901 Christos Stavrakakis
117 aed9b901 Christos Stavrakakis
    def __unicode__(self):
118 bf5c82dc Christos Stavrakakis
        return self.clustername + "(id=" + str(self.id) + ")"
119 aed9b901 Christos Stavrakakis
120 aed9b901 Christos Stavrakakis
    @property
121 aed9b901 Christos Stavrakakis
    def backend_id(self):
122 aed9b901 Christos Stavrakakis
        return self.id
123 aed9b901 Christos Stavrakakis
124 3524241a Christos Stavrakakis
    def get_client(self):
125 aed9b901 Christos Stavrakakis
        """Get or create a client. """
126 3524241a Christos Stavrakakis
        if self.offline:
127 bd40abfa Christos Stavrakakis
            raise faults.ServiceUnavailable
128 3524241a Christos Stavrakakis
        return get_rapi_client(self.id, self.hash,
129 cc50e51a Christos Stavrakakis
                               self.clustername,
130 cc50e51a Christos Stavrakakis
                               self.port,
131 3524241a Christos Stavrakakis
                               self.username,
132 3524241a Christos Stavrakakis
                               self.password)
133 3524241a Christos Stavrakakis
134 3524241a Christos Stavrakakis
    @staticmethod
135 3524241a Christos Stavrakakis
    def put_client(client):
136 3524241a Christos Stavrakakis
            put_rapi_client(client)
137 aed9b901 Christos Stavrakakis
138 aed9b901 Christos Stavrakakis
    def create_hash(self):
139 aed9b901 Christos Stavrakakis
        """Create a hash for this backend. """
140 68b952f9 Christos Stavrakakis
        sha = sha1('%s%s%s%s' %
141 68b952f9 Christos Stavrakakis
                   (self.clustername, self.port, self.username, self.password))
142 68b952f9 Christos Stavrakakis
        return sha.hexdigest()
143 aed9b901 Christos Stavrakakis
144 30e0ed74 Christos Stavrakakis
    @property
145 30e0ed74 Christos Stavrakakis
    def password(self):
146 30e0ed74 Christos Stavrakakis
        return decrypt_db_charfield(self.password_hash)
147 30e0ed74 Christos Stavrakakis
148 30e0ed74 Christos Stavrakakis
    @password.setter
149 30e0ed74 Christos Stavrakakis
    def password(self, value):
150 30e0ed74 Christos Stavrakakis
        self.password_hash = encrypt_db_charfield(value)
151 30e0ed74 Christos Stavrakakis
152 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
153 aed9b901 Christos Stavrakakis
        # Create a new hash each time a Backend is saved
154 aed9b901 Christos Stavrakakis
        old_hash = self.hash
155 aed9b901 Christos Stavrakakis
        self.hash = self.create_hash()
156 aed9b901 Christos Stavrakakis
        super(Backend, self).save(*args, **kwargs)
157 aed9b901 Christos Stavrakakis
        if self.hash != old_hash:
158 aed9b901 Christos Stavrakakis
            # Populate the new hash to the new instances
159 68b952f9 Christos Stavrakakis
            self.virtual_machines.filter(deleted=False)\
160 68b952f9 Christos Stavrakakis
                                 .update(backend_hash=self.hash)
161 aed9b901 Christos Stavrakakis
162 aed9b901 Christos Stavrakakis
    def delete(self, *args, **kwargs):
163 aed9b901 Christos Stavrakakis
        # Integrity Error if non-deleted VMs are associated with Backend
164 aed9b901 Christos Stavrakakis
        if self.virtual_machines.filter(deleted=False).count():
165 aed9b901 Christos Stavrakakis
            raise IntegrityError("Non-deleted virtual machines are associated "
166 aed9b901 Christos Stavrakakis
                                 "with backend: %s" % self)
167 aed9b901 Christos Stavrakakis
        else:
168 aed9b901 Christos Stavrakakis
            # ON_DELETE = SET NULL
169 ce9abd26 Christos Stavrakakis
            for vm in self.virtual_machines.all():
170 ce9abd26 Christos Stavrakakis
                vm.backend = None
171 ce9abd26 Christos Stavrakakis
                vm.save()
172 fd65ab41 Christos Stavrakakis
            self.virtual_machines.all().backend = None
173 82c1c610 Christos Stavrakakis
            # Remove BackendNetworks of this Backend.
174 82c1c610 Christos Stavrakakis
            # Do not use networks.all().delete(), since delete() method of
175 82c1c610 Christos Stavrakakis
            # BackendNetwork will not be called!
176 82c1c610 Christos Stavrakakis
            for net in self.networks.all():
177 82c1c610 Christos Stavrakakis
                net.delete()
178 aed9b901 Christos Stavrakakis
            super(Backend, self).delete(*args, **kwargs)
179 aed9b901 Christos Stavrakakis
180 3165f027 Christos Stavrakakis
    def __init__(self, *args, **kwargs):
181 3165f027 Christos Stavrakakis
        super(Backend, self).__init__(*args, **kwargs)
182 3165f027 Christos Stavrakakis
        if not self.pk:
183 3165f027 Christos Stavrakakis
            # Generate a unique index for the Backend
184 3165f027 Christos Stavrakakis
            indexes = Backend.objects.all().values_list('index', flat=True)
185 ce9abd26 Christos Stavrakakis
            try:
186 ce9abd26 Christos Stavrakakis
                first_free = [x for x in xrange(0, 16) if x not in indexes][0]
187 ce9abd26 Christos Stavrakakis
                self.index = first_free
188 ce9abd26 Christos Stavrakakis
            except IndexError:
189 ce9abd26 Christos Stavrakakis
                raise Exception("Can not create more than 16 backends")
190 3165f027 Christos Stavrakakis
191 bd87213f Christos Stavrakakis
    def use_hotplug(self):
192 bd87213f Christos Stavrakakis
        return self.hypervisor == "kvm" and snf_settings.GANETI_USE_HOTPLUG
193 bd87213f Christos Stavrakakis
194 bd87213f Christos Stavrakakis
    def get_create_params(self):
195 bd87213f Christos Stavrakakis
        params = deepcopy(snf_settings.GANETI_CREATEINSTANCE_KWARGS)
196 bd87213f Christos Stavrakakis
        params["hvparams"] = params.get("hvparams", {})\
197 bd87213f Christos Stavrakakis
                                   .get(self.hypervisor, {})
198 bd87213f Christos Stavrakakis
        return params
199 bd87213f Christos Stavrakakis
200 aed9b901 Christos Stavrakakis
201 fd65ab41 Christos Stavrakakis
# A backend job may be in one of the following possible states
202 fd65ab41 Christos Stavrakakis
BACKEND_STATUSES = (
203 fd65ab41 Christos Stavrakakis
    ('queued', 'request queued'),
204 fd65ab41 Christos Stavrakakis
    ('waiting', 'request waiting for locks'),
205 fd65ab41 Christos Stavrakakis
    ('canceling', 'request being canceled'),
206 fd65ab41 Christos Stavrakakis
    ('running', 'request running'),
207 fd65ab41 Christos Stavrakakis
    ('canceled', 'request canceled'),
208 fd65ab41 Christos Stavrakakis
    ('success', 'request completed successfully'),
209 fd65ab41 Christos Stavrakakis
    ('error', 'request returned error')
210 fd65ab41 Christos Stavrakakis
)
211 fd65ab41 Christos Stavrakakis
212 fd65ab41 Christos Stavrakakis
213 3416e629 Christos Stavrakakis
class QuotaHolderSerial(models.Model):
214 97fffe10 Christos Stavrakakis
    """Model representing a serial for a Quotaholder Commission.
215 97fffe10 Christos Stavrakakis

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

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

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