Statistics
| Branch: | Tag: | Revision:

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

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

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