Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (39.5 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 8d5795b4 Christos Stavrakakis
                raise Exception("Cannot 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 ba6ad346 Dionysis Grigoropoulos
    VIRTUAL_MACHINE_NAME_LENGTH = 255
299 ba6ad346 Dionysis Grigoropoulos
300 ba6ad346 Dionysis Grigoropoulos
    name = models.CharField('Virtual Machine Name',
301 ba6ad346 Dionysis Grigoropoulos
                            max_length=VIRTUAL_MACHINE_NAME_LENGTH)
302 190d155f Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=100,
303 e18c1749 Christos Stavrakakis
                              db_index=True, null=False)
304 aed9b901 Christos Stavrakakis
    backend = models.ForeignKey(Backend, null=True,
305 26515bc1 Christos Stavrakakis
                                related_name="virtual_machines",
306 26515bc1 Christos Stavrakakis
                                on_delete=models.PROTECT)
307 aed9b901 Christos Stavrakakis
    backend_hash = models.CharField(max_length=128, null=True, editable=False)
308 d174b105 Vassilios Karakoidas
    created = models.DateTimeField(auto_now_add=True)
309 d174b105 Vassilios Karakoidas
    updated = models.DateTimeField(auto_now=True)
310 936afb7b Giorgos Verigakis
    imageid = models.CharField(max_length=100, null=False)
311 22571df6 Vassilios Karakoidas
    hostid = models.CharField(max_length=100)
312 3bb0b117 Christos Stavrakakis
    flavor = models.ForeignKey(Flavor, on_delete=models.PROTECT)
313 190d155f Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
314 9ff7ca6f Giorgos Verigakis
    suspended = models.BooleanField('Administratively Suspended',
315 244c552b Giorgos Verigakis
                                    default=False)
316 004967e4 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial,
317 3bb0b117 Christos Stavrakakis
                               related_name='virtual_machine', null=True,
318 3bb0b117 Christos Stavrakakis
                               on_delete=models.SET_NULL)
319 d08a5f6f Vangelis Koukis
320 ce55f211 Kostas Papadimitriou
    # VM State
321 52194743 Vangelis Koukis
    # The following fields are volatile data, in the sense
322 52194743 Vangelis Koukis
    # that they need not be persistent in the DB, but rather
323 52194743 Vangelis Koukis
    # get generated at runtime by quering Ganeti and applying
324 52194743 Vangelis Koukis
    # updates received from Ganeti.
325 ce55f211 Kostas Papadimitriou
326 73b2b65e Vassilios Karakoidas
    # In the future they could be moved to a separate caching layer
327 73b2b65e Vassilios Karakoidas
    # and removed from the database.
328 73b2b65e Vassilios Karakoidas
    # [vkoukis] after discussion with [faidon].
329 1cfd5d4d Christos Stavrakakis
    action = models.CharField(choices=ACTIONS, max_length=30, null=True,
330 1cfd5d4d Christos Stavrakakis
                              default=None)
331 1cfd5d4d Christos Stavrakakis
    operstate = models.CharField(choices=OPER_STATES, max_length=30,
332 1cfd5d4d Christos Stavrakakis
                                 null=False, default="BUILD")
333 dfd19c2d Vassilios Karakoidas
    backendjobid = models.PositiveIntegerField(null=True)
334 f533f224 Vangelis Koukis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
335 aed9b901 Christos Stavrakakis
                                     null=True)
336 9ff7ca6f Giorgos Verigakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
337 aed9b901 Christos Stavrakakis
                                        max_length=30, null=True)
338 dfd19c2d Vassilios Karakoidas
    backendlogmsg = models.TextField(null=True)
339 19da4325 Vangelis Koukis
    buildpercentage = models.IntegerField(default=0)
340 c4e55622 Christos Stavrakakis
    backendtime = models.DateTimeField(default=datetime.datetime.min)
341 9068cd85 Georgios Gousios
342 41a7fae7 Christos Stavrakakis
    # Latest action and corresponding Ganeti job ID, for actions issued
343 41a7fae7 Christos Stavrakakis
    # by the API
344 41a7fae7 Christos Stavrakakis
    task = models.CharField(max_length=64, null=True)
345 e8d31c74 Christos Stavrakakis
    task_job_id = models.BigIntegerField(null=True)
346 41a7fae7 Christos Stavrakakis
347 3524241a Christos Stavrakakis
    def get_client(self):
348 3524241a Christos Stavrakakis
        if self.backend:
349 2cea7f38 Christos Stavrakakis
            return self.backend.get_client()
350 aed9b901 Christos Stavrakakis
        else:
351 00eb7f51 Christos Stavrakakis
            raise faults.ServiceUnavailable("VirtualMachine without backend")
352 aed9b901 Christos Stavrakakis
353 6ce1fc56 Kostas Papadimitriou
    def get_last_diagnostic(self, **filters):
354 6ce1fc56 Kostas Papadimitriou
        try:
355 6ce1fc56 Kostas Papadimitriou
            return self.diagnostics.filter()[0]
356 6ce1fc56 Kostas Papadimitriou
        except IndexError:
357 6ce1fc56 Kostas Papadimitriou
            return None
358 6ce1fc56 Kostas Papadimitriou
359 3524241a Christos Stavrakakis
    @staticmethod
360 3524241a Christos Stavrakakis
    def put_client(client):
361 3524241a Christos Stavrakakis
            put_rapi_client(client)
362 3524241a Christos Stavrakakis
363 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
364 aed9b901 Christos Stavrakakis
        # Store hash for first time saved vm
365 aed9b901 Christos Stavrakakis
        if (self.id is None or self.backend_hash == '') and self.backend:
366 aed9b901 Christos Stavrakakis
            self.backend_hash = self.backend.hash
367 aed9b901 Christos Stavrakakis
        super(VirtualMachine, self).save(*args, **kwargs)
368 aed9b901 Christos Stavrakakis
369 9ff7ca6f Giorgos Verigakis
    @property
370 924d8085 Christos Stavrakakis
    def backend_vm_id(self):
371 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
372 76ba77c1 Vangelis Koukis
        if not self.id:
373 76ba77c1 Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError("self.id is None")
374 d30f29aa Christos Stavrakakis
        return "%s%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
375 ce55f211 Kostas Papadimitriou
376 d08a5f6f Vangelis Koukis
    class Meta:
377 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
378 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
379 ce55f211 Kostas Papadimitriou
380 d08a5f6f Vangelis Koukis
    def __unicode__(self):
381 87105bf9 Christos Stavrakakis
        return u"<vm:%s@backend:%s>" % (self.id, self.backend_id)
382 d08a5f6f Vangelis Koukis
383 048fab1e Christos Stavrakakis
    # Error classes
384 048fab1e Christos Stavrakakis
    class InvalidBackendIdError(Exception):
385 048fab1e Christos Stavrakakis
        def __init__(self, value):
386 048fab1e Christos Stavrakakis
            self.value = value
387 048fab1e Christos Stavrakakis
388 048fab1e Christos Stavrakakis
        def __str__(self):
389 048fab1e Christos Stavrakakis
            return repr(self.value)
390 048fab1e Christos Stavrakakis
391 048fab1e Christos Stavrakakis
    class InvalidBackendMsgError(Exception):
392 048fab1e Christos Stavrakakis
        def __init__(self, opcode, status):
393 048fab1e Christos Stavrakakis
            self.opcode = opcode
394 048fab1e Christos Stavrakakis
            self.status = status
395 048fab1e Christos Stavrakakis
396 048fab1e Christos Stavrakakis
        def __str__(self):
397 048fab1e Christos Stavrakakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
398 048fab1e Christos Stavrakakis
                        self.status))
399 048fab1e Christos Stavrakakis
400 048fab1e Christos Stavrakakis
    class InvalidActionError(Exception):
401 048fab1e Christos Stavrakakis
        def __init__(self, action):
402 048fab1e Christos Stavrakakis
            self._action = action
403 048fab1e Christos Stavrakakis
404 048fab1e Christos Stavrakakis
        def __str__(self):
405 048fab1e Christos Stavrakakis
            return repr(str(self._action))
406 048fab1e Christos Stavrakakis
407 4ffb82dc Vassilios Karakoidas
408 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
409 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
410 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
411 3bb0b117 Christos Stavrakakis
    vm = models.ForeignKey(VirtualMachine, related_name='metadata',
412 3bb0b117 Christos Stavrakakis
                           on_delete=models.CASCADE)
413 ce55f211 Kostas Papadimitriou
414 be7b8d37 Vassilios Karakoidas
    class Meta:
415 1bd24df6 Giorgos Verigakis
        unique_together = (('meta_key', 'vm'),)
416 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
417 ce55f211 Kostas Papadimitriou
418 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
419 9ff7ca6f Giorgos Verigakis
        return u'%s: %s' % (self.meta_key, self.meta_value)
420 be7b8d37 Vassilios Karakoidas
421 be7b8d37 Vassilios Karakoidas
422 0269afd6 Giorgos Verigakis
class Network(models.Model):
423 e6a42a96 Christos Stavrakakis
    OPER_STATES = (
424 99af08a4 Christos Stavrakakis
        ('PENDING', 'Pending'),  # Unused because of lazy networks
425 f533f224 Vangelis Koukis
        ('ACTIVE', 'Active'),
426 e6a42a96 Christos Stavrakakis
        ('DELETED', 'Deleted'),
427 e6a42a96 Christos Stavrakakis
        ('ERROR', 'Error')
428 e6a42a96 Christos Stavrakakis
    )
429 e6a42a96 Christos Stavrakakis
430 e6a42a96 Christos Stavrakakis
    ACTIONS = (
431 68b952f9 Christos Stavrakakis
        ('CREATE', 'Create Network'),
432 68b952f9 Christos Stavrakakis
        ('DESTROY', 'Destroy Network'),
433 ece5581b Christos Stavrakakis
        ('ADD', 'Add server to Network'),
434 ece5581b Christos Stavrakakis
        ('REMOVE', 'Remove server from Network'),
435 e6a42a96 Christos Stavrakakis
    )
436 e6a42a96 Christos Stavrakakis
437 e6a42a96 Christos Stavrakakis
    RSAPI_STATE_FROM_OPER_STATE = {
438 e6a42a96 Christos Stavrakakis
        'PENDING': 'PENDING',
439 e6a42a96 Christos Stavrakakis
        'ACTIVE': 'ACTIVE',
440 e6a42a96 Christos Stavrakakis
        'DELETED': 'DELETED',
441 e6a42a96 Christos Stavrakakis
        'ERROR': 'ERROR'
442 e6a42a96 Christos Stavrakakis
    }
443 e6a42a96 Christos Stavrakakis
444 b7d38981 Dimitris Aragiorgis
    FLAVORS = {
445 b7d38981 Dimitris Aragiorgis
        'CUSTOM': {
446 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
447 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_BRIDGE,
448 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
449 68b952f9 Christos Stavrakakis
            'tags': None,
450 68b952f9 Christos Stavrakakis
            'desc': "Basic flavor used for a bridged network",
451 b7d38981 Dimitris Aragiorgis
        },
452 b7d38981 Dimitris Aragiorgis
        'IP_LESS_ROUTED': {
453 68b952f9 Christos Stavrakakis
            'mode': 'routed',
454 e4def9d6 Christos Stavrakakis
            'link': None,
455 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
456 68b952f9 Christos Stavrakakis
            'tags': 'ip-less-routed',
457 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for an IP-less routed network using"
458 68b952f9 Christos Stavrakakis
                    " Proxy ARP",
459 b7d38981 Dimitris Aragiorgis
        },
460 b7d38981 Dimitris Aragiorgis
        'MAC_FILTERED': {
461 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
462 68b952f9 Christos Stavrakakis
            'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
463 68b952f9 Christos Stavrakakis
            'mac_prefix': 'pool',
464 68b952f9 Christos Stavrakakis
            'tags': 'private-filtered',
465 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged networks that offer isolation"
466 68b952f9 Christos Stavrakakis
                    " via filtering packets based on their src "
467 68b952f9 Christos Stavrakakis
                    " MAC (ebtables)",
468 b7d38981 Dimitris Aragiorgis
        },
469 b7d38981 Dimitris Aragiorgis
        'PHYSICAL_VLAN': {
470 68b952f9 Christos Stavrakakis
            'mode': 'bridged',
471 68b952f9 Christos Stavrakakis
            'link': 'pool',
472 68b952f9 Christos Stavrakakis
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
473 68b952f9 Christos Stavrakakis
            'tags': 'physical-vlan',
474 68b952f9 Christos Stavrakakis
            'desc': "Flavor used for bridged network that offer isolation"
475 68b952f9 Christos Stavrakakis
                    " via dedicated physical vlan",
476 b7d38981 Dimitris Aragiorgis
        },
477 b7d38981 Dimitris Aragiorgis
    }
478 ce55f211 Kostas Papadimitriou
479 cddc2b2f Dionysis Grigoropoulos
    NETWORK_NAME_LENGTH = 128
480 cddc2b2f Dionysis Grigoropoulos
481 cddc2b2f Dionysis Grigoropoulos
    name = models.CharField('Network Name', max_length=NETWORK_NAME_LENGTH)
482 190d155f Christos Stavrakakis
    userid = models.CharField('User ID of the owner', max_length=128,
483 190d155f Christos Stavrakakis
                              null=True, db_index=True)
484 b7d38981 Dimitris Aragiorgis
    flavor = models.CharField('Flavor', max_length=32, null=False)
485 b7d38981 Dimitris Aragiorgis
    mode = models.CharField('Network Mode', max_length=16, null=True)
486 b7d38981 Dimitris Aragiorgis
    link = models.CharField('Network Link', max_length=32, null=True)
487 3165f027 Christos Stavrakakis
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
488 b7d38981 Dimitris Aragiorgis
    tags = models.CharField('Network Tags', max_length=128, null=True)
489 190d155f Christos Stavrakakis
    public = models.BooleanField(default=False, db_index=True)
490 0269afd6 Giorgos Verigakis
    created = models.DateTimeField(auto_now_add=True)
491 0269afd6 Giorgos Verigakis
    updated = models.DateTimeField(auto_now=True)
492 190d155f Christos Stavrakakis
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
493 fd65ab41 Christos Stavrakakis
    state = models.CharField(choices=OPER_STATES, max_length=32,
494 fd65ab41 Christos Stavrakakis
                             default='PENDING')
495 f533f224 Vangelis Koukis
    machines = models.ManyToManyField(VirtualMachine,
496 495e2be8 Christos Stavrakakis
                                      through='NetworkInterface')
497 fd65ab41 Christos Stavrakakis
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
498 fd65ab41 Christos Stavrakakis
                              default=None)
499 6dafedf6 Christos Stavrakakis
    drained = models.BooleanField("Drained", default=False, null=False)
500 9115d567 Christos Stavrakakis
    floating_ip_pool = models.BooleanField('Floating IP Pool', null=False,
501 b2d0830b Christos Stavrakakis
                                           default=False)
502 7b72b50d Christos Stavrakakis
    external_router = models.BooleanField(default=False)
503 004967e4 Christos Stavrakakis
    serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
504 3bb0b117 Christos Stavrakakis
                               null=True, on_delete=models.SET_NULL)
505 77f0fa63 Christos Stavrakakis
506 cfd70896 Christos Stavrakakis
    def __unicode__(self):
507 0989632e Christos Stavrakakis
        return "<Network: %s>" % str(self.id)
508 cfd70896 Christos Stavrakakis
509 e6a42a96 Christos Stavrakakis
    @property
510 e6a42a96 Christos Stavrakakis
    def backend_id(self):
511 fd65ab41 Christos Stavrakakis
        """Return the backend id by prepending backend-prefix."""
512 e6a42a96 Christos Stavrakakis
        if not self.id:
513 e6a42a96 Christos Stavrakakis
            raise Network.InvalidBackendIdError("self.id is None")
514 d30f29aa Christos Stavrakakis
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
515 e6a42a96 Christos Stavrakakis
516 e6a42a96 Christos Stavrakakis
    @property
517 e6a42a96 Christos Stavrakakis
    def backend_tag(self):
518 e6a42a96 Christos Stavrakakis
        """Return the network tag to be used in backend
519 e6a42a96 Christos Stavrakakis

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