Statistics
| Branch: | Tag: | Revision:

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

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

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

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

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