Statistics
| Branch: | Tag: | Revision:

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

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

196 97fffe10 Christos Stavrakakis
    serial:   The serial that Quotaholder assigned to this commission
197 97fffe10 Christos Stavrakakis
    pending:  Whether it has been decided to accept or reject this commission
198 97fffe10 Christos Stavrakakis
    accept:   If pending is False, this attribute indicates whether to accept
199 97fffe10 Christos Stavrakakis
              or reject this commission
200 97fffe10 Christos Stavrakakis
    resolved: Whether this commission has been accepted or rejected to
201 97fffe10 Christos Stavrakakis
              Quotaholder.
202 97fffe10 Christos Stavrakakis

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

517 e6a42a96 Christos Stavrakakis
        """
518 b7d38981 Dimitris Aragiorgis
        if self.tags:
519 b7d38981 Dimitris Aragiorgis
            return self.tags.split(',')
520 b7d38981 Dimitris Aragiorgis
        else:
521 b7d38981 Dimitris Aragiorgis
            return []
522 e6a42a96 Christos Stavrakakis
523 7cfbbf32 Christos Stavrakakis
    def create_backend_network(self, backend=None):
524 7cfbbf32 Christos Stavrakakis
        """Create corresponding BackendNetwork entries."""
525 7cfbbf32 Christos Stavrakakis
526 b2d0830b Christos Stavrakakis
        backends = [backend] if backend else\
527 b2d0830b Christos Stavrakakis
            Backend.objects.filter(offline=False)
528 7cfbbf32 Christos Stavrakakis
        for backend in backends:
529 68b952f9 Christos Stavrakakis
            backend_exists =\
530 68b952f9 Christos Stavrakakis
                BackendNetwork.objects.filter(backend=backend, network=self)\
531 68b952f9 Christos Stavrakakis
                                      .exists()
532 68b952f9 Christos Stavrakakis
            if not backend_exists:
533 ce9abd26 Christos Stavrakakis
                BackendNetwork.objects.create(backend=backend, network=self)
534 fd65ab41 Christos Stavrakakis
535 0e0a6ef9 Christos Stavrakakis
    def get_ip_pools(self, locked=True):
536 0e0a6ef9 Christos Stavrakakis
        subnets = self.subnets.filter(ipversion=4, deleted=False)\
537 0e0a6ef9 Christos Stavrakakis
                              .prefetch_related("ip_pools")
538 0e0a6ef9 Christos Stavrakakis
        return [ip_pool for subnet in subnets
539 0e0a6ef9 Christos Stavrakakis
                for ip_pool in subnet.get_ip_pools(locked=locked)]
540 77f0fa63 Christos Stavrakakis
541 3278725f Christos Stavrakakis
    def reserve_address(self, address, external=False):
542 0e0a6ef9 Christos Stavrakakis
        for ip_pool in self.get_ip_pools():
543 0e0a6ef9 Christos Stavrakakis
            if ip_pool.contains(address):
544 0e0a6ef9 Christos Stavrakakis
                ip_pool.reserve(address, external=external)
545 0e0a6ef9 Christos Stavrakakis
                ip_pool.save()
546 0e0a6ef9 Christos Stavrakakis
                return
547 0e0a6ef9 Christos Stavrakakis
        raise pools.InvalidValue("Network %s does not have an IP pool that"
548 0e0a6ef9 Christos Stavrakakis
                                 " contains address %s" % (self, address))
549 77f0fa63 Christos Stavrakakis
550 e59cda53 Christos Stavrakakis
    def release_address(self, address, external=False):
551 0e0a6ef9 Christos Stavrakakis
        for ip_pool in self.get_ip_pools():
552 0e0a6ef9 Christos Stavrakakis
            if ip_pool.contains(address):
553 e59cda53 Christos Stavrakakis
                ip_pool.put(address, external=external)
554 0e0a6ef9 Christos Stavrakakis
                ip_pool.save()
555 0e0a6ef9 Christos Stavrakakis
                return
556 0e0a6ef9 Christos Stavrakakis
        raise pools.InvalidValue("Network %s does not have an IP pool that"
557 0e0a6ef9 Christos Stavrakakis
                                 " contains address %s" % (self, address))
558 77f0fa63 Christos Stavrakakis
559 8764d304 Christos Stavrakakis
    @property
560 8764d304 Christos Stavrakakis
    def subnet4(self):
561 8764d304 Christos Stavrakakis
        return self.get_subnet(version=4)
562 8764d304 Christos Stavrakakis
563 8764d304 Christos Stavrakakis
    @property
564 8764d304 Christos Stavrakakis
    def subnet6(self):
565 8764d304 Christos Stavrakakis
        return self.get_subnet(version=6)
566 8764d304 Christos Stavrakakis
567 8764d304 Christos Stavrakakis
    def get_subnet(self, version=4):
568 8764d304 Christos Stavrakakis
        for subnet in self.subnets.all():
569 8764d304 Christos Stavrakakis
            if subnet.ipversion == version:
570 8764d304 Christos Stavrakakis
                return subnet.cidr
571 8764d304 Christos Stavrakakis
572 08e6fe1b Christos Stavrakakis
    def ip_count(self):
573 08e6fe1b Christos Stavrakakis
        """Return the total and free IPv4 addresses of the network."""
574 08e6fe1b Christos Stavrakakis
        total, free = 0, 0
575 0e0a6ef9 Christos Stavrakakis
        ip_pools = self.get_ip_pools(locked=False)
576 0e0a6ef9 Christos Stavrakakis
        for ip_pool in ip_pools:
577 0e0a6ef9 Christos Stavrakakis
            total += ip_pool.pool_size
578 0e0a6ef9 Christos Stavrakakis
            free += ip_pool.count_available()
579 08e6fe1b Christos Stavrakakis
        return total, free
580 08e6fe1b Christos Stavrakakis
581 048fab1e Christos Stavrakakis
    class InvalidBackendIdError(Exception):
582 048fab1e Christos Stavrakakis
        def __init__(self, value):
583 048fab1e Christos Stavrakakis
            self.value = value
584 048fab1e Christos Stavrakakis
585 048fab1e Christos Stavrakakis
        def __str__(self):
586 048fab1e Christos Stavrakakis
            return repr(self.value)
587 048fab1e Christos Stavrakakis
588 048fab1e Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
589 048fab1e Christos Stavrakakis
        def __init__(self, opcode, status):
590 048fab1e Christos Stavrakakis
            self.opcode = opcode
591 048fab1e Christos Stavrakakis
            self.status = status
592 048fab1e Christos Stavrakakis
593 048fab1e Christos Stavrakakis
        def __str__(self):
594 68b952f9 Christos Stavrakakis
            return repr('<opcode: %s, status: %s>'
595 68b952f9 Christos Stavrakakis
                        % (self.opcode, self.status))
596 048fab1e Christos Stavrakakis
597 048fab1e Christos Stavrakakis
    class InvalidActionError(Exception):
598 048fab1e Christos Stavrakakis
        def __init__(self, action):
599 048fab1e Christos Stavrakakis
            self._action = action
600 048fab1e Christos Stavrakakis
601 048fab1e Christos Stavrakakis
        def __str__(self):
602 048fab1e Christos Stavrakakis
            return repr(str(self._action))
603 048fab1e Christos Stavrakakis
604 864bed43 Christos Stavrakakis
605 4e3a6674 Christos Stavrakakis
class Subnet(models.Model):
606 4e3a6674 Christos Stavrakakis
    SUBNET_NAME_LENGTH = 128
607 4e3a6674 Christos Stavrakakis
608 4e3a6674 Christos Stavrakakis
    network = models.ForeignKey('Network', null=False, db_index=True,
609 4e3a6674 Christos Stavrakakis
                                related_name="subnets")
610 4e3a6674 Christos Stavrakakis
    name = models.CharField('Subnet Name', max_length=SUBNET_NAME_LENGTH,
611 a15114a0 Christos Stavrakakis
                            null=True, default="")
612 4e3a6674 Christos Stavrakakis
    ipversion = models.IntegerField('IP Version', default=4, null=False)
613 74d936dc Christos Stavrakakis
    cidr = models.CharField('Subnet', max_length=64, null=False)
614 4e3a6674 Christos Stavrakakis
    gateway = models.CharField('Gateway', max_length=64, null=True)
615 74d936dc Christos Stavrakakis
    dhcp = models.BooleanField('DHCP', default=True, null=False)
616 74d936dc Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True,
617 74d936dc Christos Stavrakakis
                                  null=False)
618 4e3a6674 Christos Stavrakakis
    host_routes = fields.SeparatedValuesField('Host Routes', null=True)
619 4e3a6674 Christos Stavrakakis
    dns_nameservers = fields.SeparatedValuesField('DNS Nameservers', null=True)
620 4e3a6674 Christos Stavrakakis
621 4e3a6674 Christos Stavrakakis
    def __unicode__(self):
622 8764d304 Christos Stavrakakis
        msg = u"<Subnet %s, Network: %s, CIDR: %s>"
623 8764d304 Christos Stavrakakis
        return msg % (self.id, self.network_id, self.cidr)
624 4e3a6674 Christos Stavrakakis
625 0e0a6ef9 Christos Stavrakakis
    def get_ip_pools(self, locked=True):
626 92d2d1ce Christos Stavrakakis
        ip_pools = self.ip_pools
627 92d2d1ce Christos Stavrakakis
        if locked:
628 92d2d1ce Christos Stavrakakis
            ip_pools = ip_pools.select_for_update()
629 0e0a6ef9 Christos Stavrakakis
        return map(lambda ip_pool: ip_pool.pool, ip_pools.all())
630 92d2d1ce Christos Stavrakakis
631 4e3a6674 Christos Stavrakakis
632 fd65ab41 Christos Stavrakakis
class BackendNetwork(models.Model):
633 fd65ab41 Christos Stavrakakis
    OPER_STATES = (
634 fd65ab41 Christos Stavrakakis
        ('PENDING', 'Pending'),
635 fd65ab41 Christos Stavrakakis
        ('ACTIVE', 'Active'),
636 fd65ab41 Christos Stavrakakis
        ('DELETED', 'Deleted'),
637 fd65ab41 Christos Stavrakakis
        ('ERROR', 'Error')
638 fd65ab41 Christos Stavrakakis
    )
639 fd65ab41 Christos Stavrakakis
640 fd65ab41 Christos Stavrakakis
    # The list of possible operations on the backend
641 fd65ab41 Christos Stavrakakis
    BACKEND_OPCODES = (
642 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_ADD', 'Create Network'),
643 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_CONNECT', 'Activate Network'),
644 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_DISCONNECT', 'Deactivate Network'),
645 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_REMOVE', 'Remove Network'),
646 fd65ab41 Christos Stavrakakis
        # These are listed here for completeness,
647 fd65ab41 Christos Stavrakakis
        # and are ignored for the time being
648 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_SET_PARAMS', 'Set Network Parameters'),
649 fd65ab41 Christos Stavrakakis
        ('OP_NETWORK_QUERY_DATA', 'Query Network Data')
650 fd65ab41 Christos Stavrakakis
    )
651 fd65ab41 Christos Stavrakakis
652 fd65ab41 Christos Stavrakakis
    # The operating state of a Netowork,
653 fd65ab41 Christos Stavrakakis
    # upon the successful completion of a backend operation.
654 fd65ab41 Christos Stavrakakis
    # IMPORTANT: Make sure all keys have a corresponding
655 fd65ab41 Christos Stavrakakis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
656 fd65ab41 Christos Stavrakakis
    OPER_STATE_FROM_OPCODE = {
657 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_ADD': 'PENDING',
658 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_CONNECT': 'ACTIVE',
659 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_DISCONNECT': 'PENDING',
660 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_REMOVE': 'DELETED',
661 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_SET_PARAMS': None,
662 fd65ab41 Christos Stavrakakis
        'OP_NETWORK_QUERY_DATA': None
663 fd65ab41 Christos Stavrakakis
    }
664 fd65ab41 Christos Stavrakakis
665 3bb0b117 Christos Stavrakakis
    network = models.ForeignKey(Network, related_name='backend_networks',
666 3bb0b117 Christos Stavrakakis
                                on_delete=models.CASCADE)
667 26515bc1 Christos Stavrakakis
    backend = models.ForeignKey(Backend, related_name='networks',
668 26515bc1 Christos Stavrakakis
                                on_delete=models.PROTECT)
669 fd65ab41 Christos Stavrakakis
    created = models.DateTimeField(auto_now_add=True)
670 fd65ab41 Christos Stavrakakis
    updated = models.DateTimeField(auto_now=True)
671 fd65ab41 Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False)
672 3165f027 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
673 fd65ab41 Christos Stavrakakis
    operstate = models.CharField(choices=OPER_STATES, max_length=30,
674 fd65ab41 Christos Stavrakakis
                                 default='PENDING')
675 fd65ab41 Christos Stavrakakis
    backendjobid = models.PositiveIntegerField(null=True)
676 fd65ab41 Christos Stavrakakis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
677 fd65ab41 Christos Stavrakakis
                                     null=True)
678 fd65ab41 Christos Stavrakakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
679 fd65ab41 Christos Stavrakakis
                                        max_length=30, null=True)
680 fd65ab41 Christos Stavrakakis
    backendlogmsg = models.TextField(null=True)
681 fd65ab41 Christos Stavrakakis
    backendtime = models.DateTimeField(null=False,
682 fd65ab41 Christos Stavrakakis
                                       default=datetime.datetime.min)
683 fd65ab41 Christos Stavrakakis
684 7cfbbf32 Christos Stavrakakis
    class Meta:
685 7cfbbf32 Christos Stavrakakis
        # Ensure one entry for each network in each backend
686 7cfbbf32 Christos Stavrakakis
        unique_together = (("network", "backend"))
687 7cfbbf32 Christos Stavrakakis
688 3165f027 Christos Stavrakakis
    def __init__(self, *args, **kwargs):
689 3165f027 Christos Stavrakakis
        """Initialize state for just created BackendNetwork instances."""
690 3165f027 Christos Stavrakakis
        super(BackendNetwork, self).__init__(*args, **kwargs)
691 3165f027 Christos Stavrakakis
        if not self.mac_prefix:
692 3165f027 Christos Stavrakakis
            # Generate the MAC prefix of the BackendNetwork, by combining
693 3165f027 Christos Stavrakakis
            # the Network prefix with the index of the Backend
694 3165f027 Christos Stavrakakis
            net_prefix = self.network.mac_prefix
695 3165f027 Christos Stavrakakis
            backend_suffix = hex(self.backend.index).replace('0x', '')
696 3165f027 Christos Stavrakakis
            mac_prefix = net_prefix + backend_suffix
697 3165f027 Christos Stavrakakis
            try:
698 3165f027 Christos Stavrakakis
                utils.validate_mac(mac_prefix + ":00:00:00")
699 3165f027 Christos Stavrakakis
            except utils.InvalidMacAddress:
700 68b952f9 Christos Stavrakakis
                raise utils.InvalidMacAddress("Invalid MAC prefix '%s'" %
701 68b952f9 Christos Stavrakakis
                                              mac_prefix)
702 3165f027 Christos Stavrakakis
            self.mac_prefix = mac_prefix
703 3165f027 Christos Stavrakakis
704 99af08a4 Christos Stavrakakis
    def __unicode__(self):
705 99af08a4 Christos Stavrakakis
        return '<%s@%s>' % (self.network, self.backend)
706 99af08a4 Christos Stavrakakis
707 f533f224 Vangelis Koukis
708 bdd0f1a6 Christos Stavrakakis
class IPAddress(models.Model):
709 bdd0f1a6 Christos Stavrakakis
    subnet = models.ForeignKey("Subnet", related_name="ips", null=False,
710 bdd0f1a6 Christos Stavrakakis
                               on_delete=models.CASCADE)
711 bdd0f1a6 Christos Stavrakakis
    network = models.ForeignKey(Network, related_name="ips", null=False,
712 bdd0f1a6 Christos Stavrakakis
                                on_delete=models.CASCADE)
713 bdd0f1a6 Christos Stavrakakis
    nic = models.ForeignKey("NetworkInterface", related_name="ips", null=True,
714 bdd0f1a6 Christos Stavrakakis
                            on_delete=models.SET_NULL)
715 bdd0f1a6 Christos Stavrakakis
    userid = models.CharField("UUID of the owner", max_length=128, null=False,
716 bdd0f1a6 Christos Stavrakakis
                              db_index=True)
717 bdd0f1a6 Christos Stavrakakis
    address = models.CharField("IP Address", max_length=64, null=False)
718 bdd0f1a6 Christos Stavrakakis
    floating_ip = models.BooleanField("Floating IP", null=False, default=False)
719 bdd0f1a6 Christos Stavrakakis
    created = models.DateTimeField(auto_now_add=True)
720 bdd0f1a6 Christos Stavrakakis
    updated = models.DateTimeField(auto_now=True)
721 bdd0f1a6 Christos Stavrakakis
    deleted = models.BooleanField(default=False, null=False)
722 bdd0f1a6 Christos Stavrakakis
723 bdd0f1a6 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial,
724 bdd0f1a6 Christos Stavrakakis
                               related_name="ips", null=True,
725 bdd0f1a6 Christos Stavrakakis
                               on_delete=models.SET_NULL)
726 bdd0f1a6 Christos Stavrakakis
727 bdd0f1a6 Christos Stavrakakis
    def __unicode__(self):
728 bdd0f1a6 Christos Stavrakakis
        ip_type = "floating" if self.floating_ip else "static"
729 bdd0f1a6 Christos Stavrakakis
        return u"<IPAddress: %s, Network: %s, Subnet: %s, Type: %s>"\
730 bdd0f1a6 Christos Stavrakakis
               % (self.address, self.network_id, self.subnet_id, ip_type)
731 bdd0f1a6 Christos Stavrakakis
732 bdd0f1a6 Christos Stavrakakis
    def in_use(self):
733 bdd0f1a6 Christos Stavrakakis
        if self.machine is None:
734 bdd0f1a6 Christos Stavrakakis
            return False
735 bdd0f1a6 Christos Stavrakakis
        else:
736 bdd0f1a6 Christos Stavrakakis
            return (not self.machine.deleted)
737 bdd0f1a6 Christos Stavrakakis
738 bdd0f1a6 Christos Stavrakakis
    class Meta:
739 bdd0f1a6 Christos Stavrakakis
        unique_together = ("network", "address")
740 bdd0f1a6 Christos Stavrakakis
741 b3a43976 Christos Stavrakakis
    @property
742 b3a43976 Christos Stavrakakis
    def ipversion(self):
743 b3a43976 Christos Stavrakakis
        return self.subnet.ipversion
744 b3a43976 Christos Stavrakakis
745 b3a43976 Christos Stavrakakis
    @property
746 b3a43976 Christos Stavrakakis
    def public(self):
747 b3a43976 Christos Stavrakakis
        return self.network.public
748 b3a43976 Christos Stavrakakis
749 08e6fe1b Christos Stavrakakis
    def release_address(self):
750 08e6fe1b Christos Stavrakakis
        """Release the IPv4 address."""
751 08e6fe1b Christos Stavrakakis
        if self.ipversion == 4:
752 08e6fe1b Christos Stavrakakis
            for pool_row in self.subnet.ip_pools.all():
753 08e6fe1b Christos Stavrakakis
                ip_pool = pool_row.pool
754 08e6fe1b Christos Stavrakakis
                if ip_pool.contains(self.address):
755 08e6fe1b Christos Stavrakakis
                    ip_pool.put(self.address)
756 08e6fe1b Christos Stavrakakis
                    ip_pool.save()
757 08e6fe1b Christos Stavrakakis
758 bdd0f1a6 Christos Stavrakakis
759 f533f224 Vangelis Koukis
class NetworkInterface(models.Model):
760 f533f224 Vangelis Koukis
    FIREWALL_PROFILES = (
761 f533f224 Vangelis Koukis
        ('ENABLED', 'Enabled'),
762 26563957 Giorgos Verigakis
        ('DISABLED', 'Disabled'),
763 26563957 Giorgos Verigakis
        ('PROTECTED', 'Protected')
764 f533f224 Vangelis Koukis
    )
765 ce55f211 Kostas Papadimitriou
766 939d71dd Christos Stavrakakis
    STATES = (
767 939d71dd Christos Stavrakakis
        ("ACTIVE", "Active"),
768 3a6be177 Christos Stavrakakis
        ("BUILD", "Building"),
769 9dcfad23 Christos Stavrakakis
        ("ERROR", "Error"),
770 3a6be177 Christos Stavrakakis
        ("DOWN", "Down"),
771 939d71dd Christos Stavrakakis
    )
772 939d71dd Christos Stavrakakis
773 cddc2b2f Dionysis Grigoropoulos
    NETWORK_IFACE_NAME_LENGTH = 128
774 cddc2b2f Dionysis Grigoropoulos
775 a15114a0 Christos Stavrakakis
    name = models.CharField('NIC name', max_length=128, null=True, default="")
776 cddc2b2f Dionysis Grigoropoulos
    userid = models.CharField("UUID of the owner",
777 cddc2b2f Dionysis Grigoropoulos
                              max_length=NETWORK_IFACE_NAME_LENGTH,
778 bdd0f1a6 Christos Stavrakakis
                              null=True, db_index=True)
779 3bb0b117 Christos Stavrakakis
    machine = models.ForeignKey(VirtualMachine, related_name='nics',
780 3bb0b117 Christos Stavrakakis
                                on_delete=models.CASCADE)
781 3bb0b117 Christos Stavrakakis
    network = models.ForeignKey(Network, related_name='nics',
782 3bb0b117 Christos Stavrakakis
                                on_delete=models.CASCADE)
783 f533f224 Vangelis Koukis
    created = models.DateTimeField(auto_now_add=True)
784 f533f224 Vangelis Koukis
    updated = models.DateTimeField(auto_now=True)
785 051e47f8 Christos Stavrakakis
    index = models.IntegerField(null=True)
786 939d71dd Christos Stavrakakis
    mac = models.CharField(max_length=32, null=True, unique=True)
787 f533f224 Vangelis Koukis
    firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
788 244c552b Giorgos Verigakis
                                        max_length=30, null=True)
789 bdd0f1a6 Christos Stavrakakis
    security_groups = models.ManyToManyField("SecurityGroup", null=True)
790 ef54eee4 Christos Stavrakakis
    state = models.CharField(max_length=32, null=False, default="ACTIVE",
791 939d71dd Christos Stavrakakis
                             choices=STATES)
792 bdd0f1a6 Christos Stavrakakis
    device_owner = models.CharField('Device owner', max_length=128, null=True)
793 bdd0f1a6 Christos Stavrakakis
794 bdd0f1a6 Christos Stavrakakis
    def __unicode__(self):
795 bdd0f1a6 Christos Stavrakakis
        return "<%s:vm:%s network:%s>" % (self.id, self.machine_id,
796 bdd0f1a6 Christos Stavrakakis
                                          self.network_id)
797 ce55f211 Kostas Papadimitriou
798 7c714455 Christos Stavrakakis
    @property
799 7c714455 Christos Stavrakakis
    def backend_uuid(self):
800 7c714455 Christos Stavrakakis
        """Return the backend id by prepending backend-prefix."""
801 7c714455 Christos Stavrakakis
        return "%snic-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
802 7c714455 Christos Stavrakakis
803 92d2d1ce Christos Stavrakakis
    @property
804 92d2d1ce Christos Stavrakakis
    def ipv4_address(self):
805 8764d304 Christos Stavrakakis
        return self.get_ip_address(version=4)
806 8764d304 Christos Stavrakakis
807 8764d304 Christos Stavrakakis
    @property
808 8764d304 Christos Stavrakakis
    def ipv6_address(self):
809 8764d304 Christos Stavrakakis
        return self.get_ip_address(version=6)
810 8764d304 Christos Stavrakakis
811 8764d304 Christos Stavrakakis
    def get_ip_address(self, version=4):
812 8764d304 Christos Stavrakakis
        for ip in self.ips.all():
813 8764d304 Christos Stavrakakis
            if ip.subnet.ipversion == version:
814 8764d304 Christos Stavrakakis
                return ip.address
815 8764d304 Christos Stavrakakis
        return None
816 92d2d1ce Christos Stavrakakis
817 9541aab5 Marios Kogias
    def get_ip_addresses_subnets(self):
818 9541aab5 Marios Kogias
        return self.ips.values_list("address", "subnet__id")
819 9541aab5 Marios Kogias
820 df8a7015 Christos Stavrakakis
821 bdd0f1a6 Christos Stavrakakis
class SecurityGroup(models.Model):
822 cddc2b2f Dionysis Grigoropoulos
    SECURITY_GROUP_NAME_LENGTH = 128
823 cddc2b2f Dionysis Grigoropoulos
    name = models.CharField('group name',
824 cddc2b2f Dionysis Grigoropoulos
                            max_length=SECURITY_GROUP_NAME_LENGTH)
825 cb7b1c23 Christos Stavrakakis
826 f533f224 Vangelis Koukis
827 03992c72 Christos Stavrakakis
class PoolTable(models.Model):
828 03992c72 Christos Stavrakakis
    available_map = models.TextField(default="", null=False)
829 03992c72 Christos Stavrakakis
    reserved_map = models.TextField(default="", null=False)
830 03992c72 Christos Stavrakakis
    size = models.IntegerField(null=False)
831 e1334eb6 Christos Stavrakakis
832 03992c72 Christos Stavrakakis
    # Optional Fields
833 03992c72 Christos Stavrakakis
    base = models.CharField(null=True, max_length=32)
834 03992c72 Christos Stavrakakis
    offset = models.IntegerField(null=True)
835 ce55f211 Kostas Papadimitriou
836 e6a42a96 Christos Stavrakakis
    class Meta:
837 e6a42a96 Christos Stavrakakis
        abstract = True
838 e6a42a96 Christos Stavrakakis
839 e6a42a96 Christos Stavrakakis
    @classmethod
840 03992c72 Christos Stavrakakis
    def get_pool(cls):
841 e6a42a96 Christos Stavrakakis
        try:
842 21c9042c Christos Stavrakakis
            pool_row = cls.objects.select_for_update().get()
843 03992c72 Christos Stavrakakis
            return pool_row.pool
844 3bdfc1be Christos Stavrakakis
        except cls.DoesNotExist:
845 03992c72 Christos Stavrakakis
            raise pools.EmptyPool
846 e6a42a96 Christos Stavrakakis
847 03992c72 Christos Stavrakakis
    @property
848 03992c72 Christos Stavrakakis
    def pool(self):
849 03992c72 Christos Stavrakakis
        return self.manager(self)
850 e6a42a96 Christos Stavrakakis
851 e6a42a96 Christos Stavrakakis
852 03992c72 Christos Stavrakakis
class BridgePoolTable(PoolTable):
853 03992c72 Christos Stavrakakis
    manager = pools.BridgePool
854 e6a42a96 Christos Stavrakakis
855 0ccb6461 Christos Stavrakakis
    def __unicode__(self):
856 d793786b Christos Stavrakakis
        return u"<BridgePool id:%s>" % self.id
857 0ccb6461 Christos Stavrakakis
858 fdc94944 Christos Stavrakakis
859 03992c72 Christos Stavrakakis
class MacPrefixPoolTable(PoolTable):
860 03992c72 Christos Stavrakakis
    manager = pools.MacPrefixPool
861 fdc94944 Christos Stavrakakis
862 0ccb6461 Christos Stavrakakis
    def __unicode__(self):
863 d793786b Christos Stavrakakis
        return u"<MACPrefixPool id:%s>" % self.id
864 0ccb6461 Christos Stavrakakis
865 fdc94944 Christos Stavrakakis
866 fdc94944 Christos Stavrakakis
class IPPoolTable(PoolTable):
867 fdc94944 Christos Stavrakakis
    manager = pools.IPPool
868 3524241a Christos Stavrakakis
869 4e3a6674 Christos Stavrakakis
    subnet = models.ForeignKey('Subnet', related_name="ip_pools",
870 4e3a6674 Christos Stavrakakis
                               db_index=True, null=True)
871 d793786b Christos Stavrakakis
872 4e3a6674 Christos Stavrakakis
    def __unicode__(self):
873 4e3a6674 Christos Stavrakakis
        return u"<IPv4AdressPool, Subnet: %s>" % self.subnet_id
874 3524241a Christos Stavrakakis
875 7b72b50d Christos Stavrakakis
876 3524241a Christos Stavrakakis
@contextmanager
877 3524241a Christos Stavrakakis
def pooled_rapi_client(obj):
878 198d91c3 Christos Stavrakakis
        if isinstance(obj, (VirtualMachine, BackendNetwork)):
879 3524241a Christos Stavrakakis
            backend = obj.backend
880 3524241a Christos Stavrakakis
        else:
881 3524241a Christos Stavrakakis
            backend = obj
882 3524241a Christos Stavrakakis
883 3524241a Christos Stavrakakis
        if backend.offline:
884 99af08a4 Christos Stavrakakis
            log.warning("Trying to connect with offline backend: %s", backend)
885 0129231a Christos Stavrakakis
            raise faults.ServiceUnavailable("Can not connect to offline"
886 0129231a Christos Stavrakakis
                                            " backend: %s" % backend)
887 3524241a Christos Stavrakakis
888 3524241a Christos Stavrakakis
        b = backend
889 3524241a Christos Stavrakakis
        client = get_rapi_client(b.id, b.hash, b.clustername, b.port,
890 3524241a Christos Stavrakakis
                                 b.username, b.password)
891 4216cd83 Christos Stavrakakis
        try:
892 4216cd83 Christos Stavrakakis
            yield client
893 4216cd83 Christos Stavrakakis
        finally:
894 4216cd83 Christos Stavrakakis
            put_rapi_client(client)
895 6ce1fc56 Kostas Papadimitriou
896 6ce1fc56 Kostas Papadimitriou
897 6ce1fc56 Kostas Papadimitriou
class VirtualMachineDiagnosticManager(models.Manager):
898 6ce1fc56 Kostas Papadimitriou
    """
899 6ce1fc56 Kostas Papadimitriou
    Custom manager for :class:`VirtualMachineDiagnostic` model.
900 6ce1fc56 Kostas Papadimitriou
    """
901 6ce1fc56 Kostas Papadimitriou
902 6ce1fc56 Kostas Papadimitriou
    # diagnostic creation helpers
903 6ce1fc56 Kostas Papadimitriou
    def create_for_vm(self, vm, level, message, **kwargs):
904 6ce1fc56 Kostas Papadimitriou
        attrs = {'machine': vm, 'level': level, 'message': message}
905 6ce1fc56 Kostas Papadimitriou
        attrs.update(kwargs)
906 6ce1fc56 Kostas Papadimitriou
        # update instance updated time
907 6ce1fc56 Kostas Papadimitriou
        self.create(**attrs)
908 6ce1fc56 Kostas Papadimitriou
        vm.save()
909 6ce1fc56 Kostas Papadimitriou
910 6ce1fc56 Kostas Papadimitriou
    def create_error(self, vm, **kwargs):
911 6ce1fc56 Kostas Papadimitriou
        self.create_for_vm(vm, 'ERROR', **kwargs)
912 6ce1fc56 Kostas Papadimitriou
913 6ce1fc56 Kostas Papadimitriou
    def create_debug(self, vm, **kwargs):
914 6ce1fc56 Kostas Papadimitriou
        self.create_for_vm(vm, 'DEBUG', **kwargs)
915 6ce1fc56 Kostas Papadimitriou
916 6ce1fc56 Kostas Papadimitriou
    def since(self, vm, created_since, **kwargs):
917 6ce1fc56 Kostas Papadimitriou
        return self.get_query_set().filter(vm=vm, created__gt=created_since,
918 68b952f9 Christos Stavrakakis
                                           **kwargs)
919 6ce1fc56 Kostas Papadimitriou
920 6ce1fc56 Kostas Papadimitriou
921 6ce1fc56 Kostas Papadimitriou
class VirtualMachineDiagnostic(models.Model):
922 6ce1fc56 Kostas Papadimitriou
    """
923 6ce1fc56 Kostas Papadimitriou
    Model to store backend information messages that relate to the state of
924 6ce1fc56 Kostas Papadimitriou
    the virtual machine.
925 6ce1fc56 Kostas Papadimitriou
    """
926 6ce1fc56 Kostas Papadimitriou
927 6ce1fc56 Kostas Papadimitriou
    TYPES = (
928 6ce1fc56 Kostas Papadimitriou
        ('ERROR', 'Error'),
929 6ce1fc56 Kostas Papadimitriou
        ('WARNING', 'Warning'),
930 6ce1fc56 Kostas Papadimitriou
        ('INFO', 'Info'),
931 6ce1fc56 Kostas Papadimitriou
        ('DEBUG', 'Debug'),
932 6ce1fc56 Kostas Papadimitriou
    )
933 6ce1fc56 Kostas Papadimitriou
934 6ce1fc56 Kostas Papadimitriou
    objects = VirtualMachineDiagnosticManager()
935 6ce1fc56 Kostas Papadimitriou
936 6ce1fc56 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True)
937 3bb0b117 Christos Stavrakakis
    machine = models.ForeignKey('VirtualMachine', related_name="diagnostics",
938 3bb0b117 Christos Stavrakakis
                                on_delete=models.CASCADE)
939 6ce1fc56 Kostas Papadimitriou
    level = models.CharField(max_length=20, choices=TYPES)
940 6ce1fc56 Kostas Papadimitriou
    source = models.CharField(max_length=100)
941 6ce1fc56 Kostas Papadimitriou
    source_date = models.DateTimeField(null=True)
942 6ce1fc56 Kostas Papadimitriou
    message = models.CharField(max_length=255)
943 6ce1fc56 Kostas Papadimitriou
    details = models.TextField(null=True)
944 6ce1fc56 Kostas Papadimitriou
945 6ce1fc56 Kostas Papadimitriou
    class Meta:
946 6ce1fc56 Kostas Papadimitriou
        ordering = ['-created']