Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (31.6 kB)

1 244c552b Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 48130e66 Georgios Gousios
#
3 48130e66 Georgios Gousios
# Redistribution and use in source and binary forms, with or without
4 48130e66 Georgios Gousios
# modification, are permitted provided that the following conditions
5 48130e66 Georgios Gousios
# are met:
6 48130e66 Georgios Gousios
#
7 48130e66 Georgios Gousios
#   1. Redistributions of source code must retain the above copyright
8 48130e66 Georgios Gousios
#      notice, this list of conditions and the following disclaimer.
9 48130e66 Georgios Gousios
#
10 48130e66 Georgios Gousios
#  2. Redistributions in binary form must reproduce the above copyright
11 48130e66 Georgios Gousios
#     notice, this list of conditions and the following disclaimer in the
12 48130e66 Georgios Gousios
#     documentation and/or other materials provided with the distribution.
13 48130e66 Georgios Gousios
#
14 48130e66 Georgios Gousios
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 48130e66 Georgios Gousios
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 48130e66 Georgios Gousios
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 48130e66 Georgios Gousios
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 48130e66 Georgios Gousios
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 48130e66 Georgios Gousios
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 48130e66 Georgios Gousios
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 48130e66 Georgios Gousios
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 48130e66 Georgios Gousios
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 48130e66 Georgios Gousios
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 48130e66 Georgios Gousios
# SUCH DAMAGE.
25 48130e66 Georgios Gousios
#
26 48130e66 Georgios Gousios
# The views and conclusions contained in the software and documentation are
27 48130e66 Georgios Gousios
# those of the authors and should not be interpreted as representing official
28 48130e66 Georgios Gousios
# policies, either expressed or implied, of GRNET S.A.
29 48130e66 Georgios Gousios
30 c4e55622 Christos Stavrakakis
import datetime
31 c4e55622 Christos Stavrakakis
32 bd87213f Christos Stavrakakis
from copy import deepcopy
33 9dbe70f8 Markos Gogoulos
from django.conf import settings
34 17935f41 Vassilios Karakoidas
from django.db import models
35 aed9b901 Christos Stavrakakis
from django.db import IntegrityError
36 3165f027 Christos Stavrakakis
37 3165f027 Christos Stavrakakis
import utils
38 3524241a Christos Stavrakakis
from contextlib import contextmanager
39 aed9b901 Christos Stavrakakis
from hashlib import sha1
40 bd40abfa Christos Stavrakakis
from snf_django.lib.api import faults
41 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 7f2dbcad Christos Stavrakakis
    objects = ForUpdateManager()
363 7f2dbcad Christos Stavrakakis
364 3524241a Christos Stavrakakis
    def get_client(self):
365 3524241a Christos Stavrakakis
        if self.backend:
366 2cea7f38 Christos Stavrakakis
            return self.backend.get_client()
367 aed9b901 Christos Stavrakakis
        else:
368 bd40abfa Christos Stavrakakis
            raise faults.ServiceUnavailable
369 aed9b901 Christos Stavrakakis
370 6ce1fc56 Kostas Papadimitriou
    def get_last_diagnostic(self, **filters):
371 6ce1fc56 Kostas Papadimitriou
        try:
372 6ce1fc56 Kostas Papadimitriou
            return self.diagnostics.filter()[0]
373 6ce1fc56 Kostas Papadimitriou
        except IndexError:
374 6ce1fc56 Kostas Papadimitriou
            return None
375 6ce1fc56 Kostas Papadimitriou
376 3524241a Christos Stavrakakis
    @staticmethod
377 3524241a Christos Stavrakakis
    def put_client(client):
378 3524241a Christos Stavrakakis
            put_rapi_client(client)
379 3524241a Christos Stavrakakis
380 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
381 aed9b901 Christos Stavrakakis
        # Store hash for first time saved vm
382 aed9b901 Christos Stavrakakis
        if (self.id is None or self.backend_hash == '') and self.backend:
383 aed9b901 Christos Stavrakakis
            self.backend_hash = self.backend.hash
384 aed9b901 Christos Stavrakakis
        super(VirtualMachine, self).save(*args, **kwargs)
385 aed9b901 Christos Stavrakakis
386 9ff7ca6f Giorgos Verigakis
    @property
387 924d8085 Christos Stavrakakis
    def backend_vm_id(self):
388 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
389 76ba77c1 Vangelis Koukis
        if not self.id:
390 76ba77c1 Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError("self.id is None")
391 d30f29aa Christos Stavrakakis
        return "%s%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
392 ce55f211 Kostas Papadimitriou
393 d08a5f6f Vangelis Koukis
    class Meta:
394 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
395 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
396 ce55f211 Kostas Papadimitriou
397 d08a5f6f Vangelis Koukis
    def __unicode__(self):
398 0989632e Christos Stavrakakis
        return "<vm: %s>" % str(self.id)
399 d08a5f6f Vangelis Koukis
400 048fab1e Christos Stavrakakis
    # Error classes
401 048fab1e Christos Stavrakakis
    class InvalidBackendIdError(Exception):
402 048fab1e Christos Stavrakakis
        def __init__(self, value):
403 048fab1e Christos Stavrakakis
            self.value = value
404 048fab1e Christos Stavrakakis
405 048fab1e Christos Stavrakakis
        def __str__(self):
406 048fab1e Christos Stavrakakis
            return repr(self.value)
407 048fab1e Christos Stavrakakis
408 048fab1e Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
409 048fab1e Christos Stavrakakis
        def __init__(self, opcode, status):
410 048fab1e Christos Stavrakakis
            self.opcode = opcode
411 048fab1e Christos Stavrakakis
            self.status = status
412 048fab1e Christos Stavrakakis
413 048fab1e Christos Stavrakakis
        def __str__(self):
414 048fab1e Christos Stavrakakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
415 048fab1e Christos Stavrakakis
                        self.status))
416 048fab1e Christos Stavrakakis
417 048fab1e Christos Stavrakakis
    class InvalidActionError(Exception):
418 048fab1e Christos Stavrakakis
        def __init__(self, action):
419 048fab1e Christos Stavrakakis
            self._action = action
420 048fab1e Christos Stavrakakis
421 048fab1e Christos Stavrakakis
        def __str__(self):
422 048fab1e Christos Stavrakakis
            return repr(str(self._action))
423 048fab1e Christos Stavrakakis
424 4ffb82dc Vassilios Karakoidas
425 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
426 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
427 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
428 7cc3c7d9 Giorgos Verigakis
    vm = models.ForeignKey(VirtualMachine, related_name='metadata')
429 ce55f211 Kostas Papadimitriou
430 be7b8d37 Vassilios Karakoidas
    class Meta:
431 1bd24df6 Giorgos Verigakis
        unique_together = (('meta_key', 'vm'),)
432 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
433 ce55f211 Kostas Papadimitriou
434 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
435 9ff7ca6f Giorgos Verigakis
        return u'%s: %s' % (self.meta_key, self.meta_value)
436 be7b8d37 Vassilios Karakoidas
437 be7b8d37 Vassilios Karakoidas
438 0269afd6 Giorgos Verigakis
class Network(models.Model):
439 e6a42a96 Christos Stavrakakis
    OPER_STATES = (
440 99af08a4 Christos Stavrakakis
        ('PENDING', 'Pending'),  # Unused because of lazy networks
441 f533f224 Vangelis Koukis
        ('ACTIVE', 'Active'),
442 e6a42a96 Christos Stavrakakis
        ('DELETED', 'Deleted'),
443 e6a42a96 Christos Stavrakakis
        ('ERROR', 'Error')
444 e6a42a96 Christos Stavrakakis
    )
445 e6a42a96 Christos Stavrakakis
446 e6a42a96 Christos Stavrakakis
    ACTIONS = (
447 68b952f9 Christos Stavrakakis
        ('CREATE', 'Create Network'),
448 68b952f9 Christos Stavrakakis
        ('DESTROY', 'Destroy Network'),
449 ece5581b Christos Stavrakakis
        ('ADD', 'Add server to Network'),
450 ece5581b Christos Stavrakakis
        ('REMOVE', 'Remove server from Network'),
451 e6a42a96 Christos Stavrakakis
    )
452 e6a42a96 Christos Stavrakakis
453 e6a42a96 Christos Stavrakakis
    RSAPI_STATE_FROM_OPER_STATE = {
454 e6a42a96 Christos Stavrakakis
        'PENDING': 'PENDING',
455 e6a42a96 Christos Stavrakakis
        'ACTIVE': 'ACTIVE',
456 e6a42a96 Christos Stavrakakis
        'DELETED': 'DELETED',
457 e6a42a96 Christos Stavrakakis
        'ERROR': 'ERROR'
458 e6a42a96 Christos Stavrakakis
    }
459 e6a42a96 Christos Stavrakakis
460 b7d38981 Dimitris Aragiorgis
    FLAVORS = {
461 b7d38981 Dimitris Aragiorgis
        'CUSTOM': {
462 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
463 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_BRIDGE,
464 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
465 68b952f9 Christos Stavrakakis
            'tags': None,
466 68b952f9 Christos Stavrakakis
            'desc': "Basic flavor used for a bridged network",
467 b7d38981 Dimitris Aragiorgis
        },
468 b7d38981 Dimitris Aragiorgis
        'IP_LESS_ROUTED': {
469 68b952f9 Christos Stavrakakis
            'mode': 'routed',
470 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_ROUTING_TABLE,
471 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
472 68b952f9 Christos Stavrakakis
            'tags': 'ip-less-routed',
473 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for an IP-less routed network using"
474 68b952f9 Christos Stavrakakis
                    " Proxy ARP",
475 b7d38981 Dimitris Aragiorgis
        },
476 b7d38981 Dimitris Aragiorgis
        'MAC_FILTERED': {
477 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
478 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
479 68b952f9 Christos Stavrakakis
            'mac_prefix': 'pool',
480 68b952f9 Christos Stavrakakis
            'tags': 'private-filtered',
481 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged networks that offer isolation"
482 68b952f9 Christos Stavrakakis
                    " via filtering packets based on their src "
483 68b952f9 Christos Stavrakakis
                    " MAC (ebtables)",
484 b7d38981 Dimitris Aragiorgis
        },
485 b7d38981 Dimitris Aragiorgis
        'PHYSICAL_VLAN': {
486 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
487 68b952f9 Christos Stavrakakis
            'link': 'pool',
488 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
489 68b952f9 Christos Stavrakakis
            'tags': 'physical-vlan',
490 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged network that offer isolation"
491 68b952f9 Christos Stavrakakis
                    " via dedicated physical vlan",
492 b7d38981 Dimitris Aragiorgis
        },
493 b7d38981 Dimitris Aragiorgis
    }
494 ce55f211 Kostas Papadimitriou
495 e6a42a96 Christos Stavrakakis
    name = models.CharField('Network Name', max_length=128)
496 190d155f Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=128,
497 190d155f Christos Stavrakakis
                              null=True, db_index=True)
498 5aeb4e93 Christos Stavrakakis
    # subnet will be null for IPv6 only networks
499 5aeb4e93 Christos Stavrakakis
    subnet = models.CharField('Subnet', max_length=32, null=True)
500 5aeb4e93 Christos Stavrakakis
    # subnet6 will be null for IPv4 only networks
501 cc3f266e Christos Stavrakakis
    subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True)
502 e6a42a96 Christos Stavrakakis
    gateway = models.CharField('Gateway', max_length=32, null=True)
503 cc3f266e Christos Stavrakakis
    gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True)
504 e6a42a96 Christos Stavrakakis
    dhcp = models.BooleanField('DHCP', default=True)
505 b7d38981 Dimitris Aragiorgis
    flavor = models.CharField('Flavor', max_length=32, null=False)
506 b7d38981 Dimitris Aragiorgis
    mode = models.CharField('Network Mode', max_length=16, null=True)
507 b7d38981 Dimitris Aragiorgis
    link = models.CharField('Network Link', max_length=32, null=True)
508 3165f027 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
509 b7d38981 Dimitris Aragiorgis
    tags = models.CharField('Network Tags', max_length=128, null=True)
510 190d155f Christos Stavrakakis
    public = models.BooleanField(default=False, db_index=True)
511 0269afd6 Giorgos Verigakis
    created = models.DateTimeField(auto_now_add=True)
512 0269afd6 Giorgos Verigakis
    updated = models.DateTimeField(auto_now=True)
513 190d155f Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
514 fd65ab41 Christos Stavrakakis
    state = models.CharField(choices=OPER_STATES, max_length=32,
515 fd65ab41 Christos Stavrakakis
                             default='PENDING')
516 f533f224 Vangelis Koukis
    machines = models.ManyToManyField(VirtualMachine,
517 244c552b Giorgos Verigakis
                                      through='NetworkInterface')
518 fd65ab41 Christos Stavrakakis
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
519 fd65ab41 Christos Stavrakakis
                              default=None)
520 6dafedf6 Christos Stavrakakis
    drained = models.BooleanField("Drained", default=False, null=False)
521 9115d567 Christos Stavrakakis
    floating_ip_pool = models.BooleanField('Floating IP Pool', null=False,
522 b2d0830b Christos Stavrakakis
                                           default=False)
523 fdc94944 Christos Stavrakakis
    pool = models.OneToOneField('IPPoolTable', related_name='network',
524 b2d0830b Christos Stavrakakis
                                default=lambda: IPPoolTable.objects.create(
525 b2d0830b Christos Stavrakakis
                                                            available_map='',
526 b2d0830b Christos Stavrakakis
                                                            reserved_map='',
527 b2d0830b Christos Stavrakakis
                                                            size=0),
528 b2d0830b Christos Stavrakakis
                                null=True)
529 004967e4 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
530 004967e4 Christos Stavrakakis
                               null=True)
531 77f0fa63 Christos Stavrakakis
532 864bed43 Christos Stavrakakis
    objects = ForUpdateManager()
533 77f0fa63 Christos Stavrakakis
534 cfd70896 Christos Stavrakakis
    def __unicode__(self):
535 0989632e Christos Stavrakakis
        return "<Network: %s>" % str(self.id)
536 cfd70896 Christos Stavrakakis
537 e6a42a96 Christos Stavrakakis
    @property
538 e6a42a96 Christos Stavrakakis
    def backend_id(self):
539 fd65ab41 Christos Stavrakakis
        """Return the backend id by prepending backend-prefix."""
540 e6a42a96 Christos Stavrakakis
        if not self.id:
541 e6a42a96 Christos Stavrakakis
            raise Network.InvalidBackendIdError("self.id is None")
542 d30f29aa Christos Stavrakakis
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
543 e6a42a96 Christos Stavrakakis
544 e6a42a96 Christos Stavrakakis
    @property
545 e6a42a96 Christos Stavrakakis
    def backend_tag(self):
546 e6a42a96 Christos Stavrakakis
        """Return the network tag to be used in backend
547 e6a42a96 Christos Stavrakakis

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