Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16.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 9dbe70f8 Markos Gogoulos
from django.conf import settings
33 17935f41 Vassilios Karakoidas
from django.db import models
34 aed9b901 Christos Stavrakakis
from django.db import IntegrityError
35 aed9b901 Christos Stavrakakis
36 aed9b901 Christos Stavrakakis
from hashlib import sha1
37 aed9b901 Christos Stavrakakis
from synnefo.api.faults import ServiceUnavailable
38 aed9b901 Christos Stavrakakis
from synnefo.util.rapi import GanetiRapiClient
39 aed9b901 Christos Stavrakakis
40 aed9b901 Christos Stavrakakis
41 aed9b901 Christos Stavrakakis
BACKEND_CLIENTS = {}    #{hash:Backend client}
42 aed9b901 Christos Stavrakakis
BACKEND_HASHES = {}     #{Backend.id:hash}
43 aed9b901 Christos Stavrakakis
44 aed9b901 Christos Stavrakakis
def get_client(hash, backend):
45 aed9b901 Christos Stavrakakis
    """Get a cached backend client or create a new one.
46 aed9b901 Christos Stavrakakis

47 aed9b901 Christos Stavrakakis
    @param hash: The hash of the backend
48 aed9b901 Christos Stavrakakis
    @param backend: Either a backend object or backend ID
49 aed9b901 Christos Stavrakakis
    """
50 aed9b901 Christos Stavrakakis
51 aed9b901 Christos Stavrakakis
    if backend is None:
52 aed9b901 Christos Stavrakakis
        raise Exception("Backend is None. Cannot create a client.")
53 aed9b901 Christos Stavrakakis
54 aed9b901 Christos Stavrakakis
    if hash in BACKEND_CLIENTS:
55 aed9b901 Christos Stavrakakis
        # Return cached client
56 aed9b901 Christos Stavrakakis
        return BACKEND_CLIENTS[hash]
57 aed9b901 Christos Stavrakakis
58 aed9b901 Christos Stavrakakis
    # Always get a new instance to ensure latest credentials
59 aed9b901 Christos Stavrakakis
    if isinstance(backend, Backend):
60 aed9b901 Christos Stavrakakis
        backend = backend.id
61 aed9b901 Christos Stavrakakis
    (credentials,) = Backend.objects.filter(id=backend).values_list('hash',
62 aed9b901 Christos Stavrakakis
                                'clustername', 'port', 'username', 'password')
63 aed9b901 Christos Stavrakakis
64 aed9b901 Christos Stavrakakis
    hash, clustername, port, user, password = credentials
65 aed9b901 Christos Stavrakakis
66 aed9b901 Christos Stavrakakis
    # Check client for updated hash
67 aed9b901 Christos Stavrakakis
    if hash in BACKEND_CLIENTS:
68 aed9b901 Christos Stavrakakis
        return BACKEND_CLIENTS[hash]
69 aed9b901 Christos Stavrakakis
70 aed9b901 Christos Stavrakakis
    # Delete old version of the client
71 aed9b901 Christos Stavrakakis
    if backend in BACKEND_HASHES:
72 aed9b901 Christos Stavrakakis
        del BACKEND_CLIENTS[BACKEND_HASHES[backend]]
73 aed9b901 Christos Stavrakakis
74 aed9b901 Christos Stavrakakis
    # Create the new client
75 aed9b901 Christos Stavrakakis
    client = GanetiRapiClient(clustername, port, user, password)
76 aed9b901 Christos Stavrakakis
77 aed9b901 Christos Stavrakakis
    # Store the client and the hash
78 aed9b901 Christos Stavrakakis
    BACKEND_CLIENTS[hash] = client
79 aed9b901 Christos Stavrakakis
    BACKEND_HASHES[backend] = hash
80 aed9b901 Christos Stavrakakis
81 aed9b901 Christos Stavrakakis
    return client
82 aed9b901 Christos Stavrakakis
83 aed9b901 Christos Stavrakakis
84 aed9b901 Christos Stavrakakis
def clear_client_cache():
85 aed9b901 Christos Stavrakakis
    BACKEND_CLIENTS.clear()
86 aed9b901 Christos Stavrakakis
    BACKEND_HASHES.clear()
87 7b7f9205 Vassilios Karakoidas
88 f5ad2956 Markos Gogoulos
89 e7164ebc Vassilios Karakoidas
class Flavor(models.Model):
90 13b3c5ff Vassilios Karakoidas
    cpu = models.IntegerField('Number of CPUs', default=0)
91 c909cbbd Giorgos Verigakis
    ram = models.IntegerField('RAM size in MiB', default=0)
92 c909cbbd Giorgos Verigakis
    disk = models.IntegerField('Disk size in GiB', default=0)
93 c909cbbd Giorgos Verigakis
    disk_template = models.CharField('Disk template', max_length=32,
94 9ff7ca6f Giorgos Verigakis
            default=settings.DEFAULT_GANETI_DISK_TEMPLATE)
95 2c60cbef Giorgos Verigakis
    deleted = models.BooleanField('Deleted', default=False)
96 ce55f211 Kostas Papadimitriou
97 be7b8d37 Vassilios Karakoidas
    class Meta:
98 dcfc6c2d Vangelis Koukis
        verbose_name = u'Virtual machine flavor'
99 b1af40e2 Giorgos Verigakis
        unique_together = ('cpu', 'ram', 'disk', 'disk_template')
100 ce55f211 Kostas Papadimitriou
101 9ff7ca6f Giorgos Verigakis
    @property
102 9ff7ca6f Giorgos Verigakis
    def name(self):
103 f0f4edac Vassilios Karakoidas
        """Returns flavor name (generated)"""
104 d08a5f6f Vangelis Koukis
        return u'C%dR%dD%d' % (self.cpu, self.ram, self.disk)
105 ce55f211 Kostas Papadimitriou
106 46547e14 Faidon Liambotis
    def __unicode__(self):
107 46547e14 Faidon Liambotis
        return self.name
108 452d2391 Vassilios Karakoidas
109 46547e14 Faidon Liambotis
110 aed9b901 Christos Stavrakakis
class BackendQuerySet(models.query.QuerySet):
111 aed9b901 Christos Stavrakakis
    def delete(self):
112 aed9b901 Christos Stavrakakis
        for backend in self._clone():
113 aed9b901 Christos Stavrakakis
            backend.delete()
114 aed9b901 Christos Stavrakakis
115 aed9b901 Christos Stavrakakis
class ProtectDeleteManager(models.Manager):
116 aed9b901 Christos Stavrakakis
    def get_query_set(self):
117 aed9b901 Christos Stavrakakis
        return BackendQuerySet(self.model, using=self._db)
118 aed9b901 Christos Stavrakakis
119 aed9b901 Christos Stavrakakis
120 aed9b901 Christos Stavrakakis
class Backend(models.Model):
121 aed9b901 Christos Stavrakakis
    clustername = models.CharField('Cluster Name', max_length=128, unique=True)
122 aed9b901 Christos Stavrakakis
    port = models.PositiveIntegerField('Port', default=5080)
123 aed9b901 Christos Stavrakakis
    username = models.CharField('Username', max_length=64, blank=True,
124 aed9b901 Christos Stavrakakis
                                null=True)
125 aed9b901 Christos Stavrakakis
    password = models.CharField('Password', max_length=64, blank=True,
126 aed9b901 Christos Stavrakakis
                                null=True)
127 aed9b901 Christos Stavrakakis
    # Sha1 is up to 40 characters long
128 aed9b901 Christos Stavrakakis
    hash = models.CharField('Hash', max_length=40, editable=False, null=False)
129 aed9b901 Christos Stavrakakis
    drained = models.BooleanField('Drained', default=False, null=False)
130 aed9b901 Christos Stavrakakis
    offline = models.BooleanField('Offline', default=False, null=False)
131 aed9b901 Christos Stavrakakis
    # Last refresh of backend resources
132 aed9b901 Christos Stavrakakis
    updated = models.DateTimeField(auto_now_add=True)
133 aed9b901 Christos Stavrakakis
    # Backend resources
134 aed9b901 Christos Stavrakakis
    mfree = models.PositiveIntegerField('Free Memory', default=0, null=False)
135 aed9b901 Christos Stavrakakis
    mtotal = models.PositiveIntegerField('Total Memory', default=0, null=False)
136 aed9b901 Christos Stavrakakis
    dfree = models.PositiveIntegerField('Free Disk', default=0, null=False)
137 aed9b901 Christos Stavrakakis
    dtotal = models.PositiveIntegerField('Total Disk', default=0, null=False)
138 aed9b901 Christos Stavrakakis
    pinst_cnt = models.PositiveIntegerField('Primary Instances', default=0,
139 aed9b901 Christos Stavrakakis
                                            null=False)
140 aed9b901 Christos Stavrakakis
    ctotal = models.PositiveIntegerField('Total number of logical processors',
141 aed9b901 Christos Stavrakakis
                                         default=0, null=False)
142 aed9b901 Christos Stavrakakis
    # Custom object manager to protect from cascade delete
143 aed9b901 Christos Stavrakakis
    objects = ProtectDeleteManager()
144 aed9b901 Christos Stavrakakis
145 aed9b901 Christos Stavrakakis
    class Meta:
146 aed9b901 Christos Stavrakakis
        verbose_name = u'Backend'
147 aed9b901 Christos Stavrakakis
        ordering = ["clustername"]
148 aed9b901 Christos Stavrakakis
149 aed9b901 Christos Stavrakakis
    def __unicode__(self):
150 aed9b901 Christos Stavrakakis
        return self.clustername
151 aed9b901 Christos Stavrakakis
152 aed9b901 Christos Stavrakakis
    @property
153 aed9b901 Christos Stavrakakis
    def backend_id(self):
154 aed9b901 Christos Stavrakakis
        return self.id
155 aed9b901 Christos Stavrakakis
156 aed9b901 Christos Stavrakakis
    @property
157 aed9b901 Christos Stavrakakis
    def client(self):
158 aed9b901 Christos Stavrakakis
        """Get or create a client. """
159 aed9b901 Christos Stavrakakis
        if not self.offline:
160 aed9b901 Christos Stavrakakis
            return get_client(self.hash, self)
161 aed9b901 Christos Stavrakakis
        else:
162 aed9b901 Christos Stavrakakis
            raise ServiceUnavailable
163 aed9b901 Christos Stavrakakis
164 aed9b901 Christos Stavrakakis
    def create_hash(self):
165 aed9b901 Christos Stavrakakis
        """Create a hash for this backend. """
166 aed9b901 Christos Stavrakakis
        return sha1('%s%s%s%s' % \
167 aed9b901 Christos Stavrakakis
                (self.clustername, self.port, self.username, self.password)) \
168 aed9b901 Christos Stavrakakis
                .hexdigest()
169 aed9b901 Christos Stavrakakis
170 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
171 aed9b901 Christos Stavrakakis
        # Create a new hash each time a Backend is saved
172 aed9b901 Christos Stavrakakis
        old_hash = self.hash
173 aed9b901 Christos Stavrakakis
        self.hash = self.create_hash()
174 aed9b901 Christos Stavrakakis
        super(Backend, self).save(*args, **kwargs)
175 aed9b901 Christos Stavrakakis
        if self.hash != old_hash:
176 aed9b901 Christos Stavrakakis
            # Populate the new hash to the new instances
177 aed9b901 Christos Stavrakakis
            self.virtual_machines.filter(deleted=False).update(backend_hash=self.hash)
178 aed9b901 Christos Stavrakakis
179 aed9b901 Christos Stavrakakis
    def delete(self, *args, **kwargs):
180 aed9b901 Christos Stavrakakis
        # Integrity Error if non-deleted VMs are associated with Backend
181 aed9b901 Christos Stavrakakis
        if self.virtual_machines.filter(deleted=False).count():
182 aed9b901 Christos Stavrakakis
            raise IntegrityError("Non-deleted virtual machines are associated "
183 aed9b901 Christos Stavrakakis
                                 "with backend: %s" % self)
184 aed9b901 Christos Stavrakakis
        else:
185 aed9b901 Christos Stavrakakis
            # ON_DELETE = SET NULL
186 aed9b901 Christos Stavrakakis
            self.virtual_machines.all().backend=None
187 aed9b901 Christos Stavrakakis
            super(Backend, self).delete(*args, **kwargs)
188 aed9b901 Christos Stavrakakis
189 aed9b901 Christos Stavrakakis
190 07f3219d Vassilios Karakoidas
class VirtualMachine(models.Model):
191 c92af313 Vangelis Koukis
    # The list of possible actions for a VM
192 d08a5f6f Vangelis Koukis
    ACTIONS = (
193 d08a5f6f Vangelis Koukis
       ('CREATE', 'Create VM'),
194 d08a5f6f Vangelis Koukis
       ('START', 'Start VM'),
195 d08a5f6f Vangelis Koukis
       ('STOP', 'Shutdown VM'),
196 d08a5f6f Vangelis Koukis
       ('SUSPEND', 'Admin Suspend VM'),
197 d08a5f6f Vangelis Koukis
       ('REBOOT', 'Reboot VM'),
198 d08a5f6f Vangelis Koukis
       ('DESTROY', 'Destroy VM')
199 d08a5f6f Vangelis Koukis
    )
200 ce55f211 Kostas Papadimitriou
201 c92af313 Vangelis Koukis
    # The internal operating state of a VM
202 d08a5f6f Vangelis Koukis
    OPER_STATES = (
203 d08a5f6f Vangelis Koukis
        ('BUILD', 'Queued for creation'),
204 d08a5f6f Vangelis Koukis
        ('ERROR', 'Creation failed'),
205 d08a5f6f Vangelis Koukis
        ('STOPPED', 'Stopped'),
206 d08a5f6f Vangelis Koukis
        ('STARTED', 'Started'),
207 d08a5f6f Vangelis Koukis
        ('DESTROYED', 'Destroyed')
208 d08a5f6f Vangelis Koukis
    )
209 ce55f211 Kostas Papadimitriou
210 c92af313 Vangelis Koukis
    # The list of possible operations on the backend
211 d08a5f6f Vangelis Koukis
    BACKEND_OPCODES = (
212 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_CREATE', 'Create Instance'),
213 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_REMOVE', 'Remove Instance'),
214 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_STARTUP', 'Startup Instance'),
215 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_SHUTDOWN', 'Shutdown Instance'),
216 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_REBOOT', 'Reboot Instance'),
217 26fef2b9 Vangelis Koukis
218 26fef2b9 Vangelis Koukis
        # These are listed here for completeness,
219 26fef2b9 Vangelis Koukis
        # and are ignored for the time being
220 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_SET_PARAMS', 'Set Instance Parameters'),
221 26fef2b9 Vangelis Koukis
        ('OP_INSTANCE_QUERY_DATA', 'Query Instance Data'),
222 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REINSTALL', 'Reinstall Instance'),
223 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_ACTIVATE_DISKS', 'Activate Disks'),
224 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_DEACTIVATE_DISKS', 'Deactivate Disks'),
225 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_REPLACE_DISKS', 'Replace Disks'),
226 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_MIGRATE', 'Migrate Instance'),
227 21d8adbf Vangelis Koukis
        ('OP_INSTANCE_CONSOLE', 'Get Instance Console'),
228 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_RECREATE_DISKS', 'Recreate Disks'),
229 41303ed0 Vangelis Koukis
        ('OP_INSTANCE_FAILOVER', 'Failover Instance')
230 d08a5f6f Vangelis Koukis
    )
231 ce55f211 Kostas Papadimitriou
232 c92af313 Vangelis Koukis
    # A backend job may be in one of the following possible states
233 d08a5f6f Vangelis Koukis
    BACKEND_STATUSES = (
234 d08a5f6f Vangelis Koukis
        ('queued', 'request queued'),
235 d08a5f6f Vangelis Koukis
        ('waiting', 'request waiting for locks'),
236 d08a5f6f Vangelis Koukis
        ('canceling', 'request being canceled'),
237 d08a5f6f Vangelis Koukis
        ('running', 'request running'),
238 d08a5f6f Vangelis Koukis
        ('canceled', 'request canceled'),
239 d08a5f6f Vangelis Koukis
        ('success', 'request completed successfully'),
240 d08a5f6f Vangelis Koukis
        ('error', 'request returned error')
241 d08a5f6f Vangelis Koukis
    )
242 d08a5f6f Vangelis Koukis
243 d08a5f6f Vangelis Koukis
    # The operating state of a VM,
244 d08a5f6f Vangelis Koukis
    # upon the successful completion of a backend operation.
245 21d8adbf Vangelis Koukis
    # IMPORTANT: Make sure all keys have a corresponding
246 21d8adbf Vangelis Koukis
    # entry in BACKEND_OPCODES if you update this field, see #1035, #1111.
247 d08a5f6f Vangelis Koukis
    OPER_STATE_FROM_OPCODE = {
248 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_CREATE': 'STARTED',
249 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_REMOVE': 'DESTROYED',
250 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_STARTUP': 'STARTED',
251 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_SHUTDOWN': 'STOPPED',
252 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_REBOOT': 'STARTED',
253 26fef2b9 Vangelis Koukis
        'OP_INSTANCE_SET_PARAMS': None,
254 8490bf84 Georgios Gousios
        'OP_INSTANCE_QUERY_DATA': None,
255 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REINSTALL': None,
256 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_ACTIVATE_DISKS': None,
257 8490bf84 Georgios Gousios
        'OP_INSTANCE_DEACTIVATE_DISKS': None,
258 1bce82c0 Christos Stavrakakis
        'OP_INSTANCE_REPLACE_DISKS': None,
259 8490bf84 Georgios Gousios
        'OP_INSTANCE_MIGRATE': None,
260 8490bf84 Georgios Gousios
        'OP_INSTANCE_CONSOLE': None,
261 41303ed0 Vangelis Koukis
        'OP_INSTANCE_RECREATE_DISKS': None,
262 41303ed0 Vangelis Koukis
        'OP_INSTANCE_FAILOVER': None
263 d08a5f6f Vangelis Koukis
    }
264 d08a5f6f Vangelis Koukis
265 c92af313 Vangelis Koukis
    # This dictionary contains the correspondence between
266 c92af313 Vangelis Koukis
    # internal operating states and Server States as defined
267 c92af313 Vangelis Koukis
    # by the Rackspace API.
268 d08a5f6f Vangelis Koukis
    RSAPI_STATE_FROM_OPER_STATE = {
269 d08a5f6f Vangelis Koukis
        "BUILD": "BUILD",
270 d08a5f6f Vangelis Koukis
        "ERROR": "ERROR",
271 d08a5f6f Vangelis Koukis
        "STOPPED": "STOPPED",
272 d08a5f6f Vangelis Koukis
        "STARTED": "ACTIVE",
273 d08a5f6f Vangelis Koukis
        "DESTROYED": "DELETED"
274 d08a5f6f Vangelis Koukis
    }
275 d08a5f6f Vangelis Koukis
276 4daac449 Vassilios Karakoidas
    name = models.CharField('Virtual Machine Name', max_length=255)
277 244c552b Giorgos Verigakis
    userid = models.CharField('User ID of the owner', max_length=100)
278 aed9b901 Christos Stavrakakis
    backend = models.ForeignKey(Backend, null=True,
279 aed9b901 Christos Stavrakakis
                                related_name="virtual_machines",)
280 aed9b901 Christos Stavrakakis
    backend_hash = models.CharField(max_length=128, null=True, editable=False)
281 d174b105 Vassilios Karakoidas
    created = models.DateTimeField(auto_now_add=True)
282 d174b105 Vassilios Karakoidas
    updated = models.DateTimeField(auto_now=True)
283 936afb7b Giorgos Verigakis
    imageid = models.CharField(max_length=100, null=False)
284 22571df6 Vassilios Karakoidas
    hostid = models.CharField(max_length=100)
285 46547e14 Faidon Liambotis
    flavor = models.ForeignKey(Flavor)
286 c750d1d6 Vangelis Koukis
    deleted = models.BooleanField('Deleted', default=False)
287 9ff7ca6f Giorgos Verigakis
    suspended = models.BooleanField('Administratively Suspended',
288 244c552b Giorgos Verigakis
                                    default=False)
289 d08a5f6f Vangelis Koukis
290 ce55f211 Kostas Papadimitriou
    # VM State
291 52194743 Vangelis Koukis
    # The following fields are volatile data, in the sense
292 52194743 Vangelis Koukis
    # that they need not be persistent in the DB, but rather
293 52194743 Vangelis Koukis
    # get generated at runtime by quering Ganeti and applying
294 52194743 Vangelis Koukis
    # updates received from Ganeti.
295 ce55f211 Kostas Papadimitriou
296 73b2b65e Vassilios Karakoidas
    # In the future they could be moved to a separate caching layer
297 73b2b65e Vassilios Karakoidas
    # and removed from the database.
298 73b2b65e Vassilios Karakoidas
    # [vkoukis] after discussion with [faidon].
299 dfd19c2d Vassilios Karakoidas
    action = models.CharField(choices=ACTIONS, max_length=30, null=True)
300 dfd19c2d Vassilios Karakoidas
    operstate = models.CharField(choices=OPER_STATES, max_length=30, null=True)
301 dfd19c2d Vassilios Karakoidas
    backendjobid = models.PositiveIntegerField(null=True)
302 f533f224 Vangelis Koukis
    backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30,
303 aed9b901 Christos Stavrakakis
                                     null=True)
304 9ff7ca6f Giorgos Verigakis
    backendjobstatus = models.CharField(choices=BACKEND_STATUSES,
305 aed9b901 Christos Stavrakakis
                                        max_length=30, null=True)
306 dfd19c2d Vassilios Karakoidas
    backendlogmsg = models.TextField(null=True)
307 19da4325 Vangelis Koukis
    buildpercentage = models.IntegerField(default=0)
308 c4e55622 Christos Stavrakakis
    backendtime = models.DateTimeField(default=datetime.datetime.min)
309 9068cd85 Georgios Gousios
310 aed9b901 Christos Stavrakakis
    @property
311 aed9b901 Christos Stavrakakis
    def client(self):
312 aed9b901 Christos Stavrakakis
        if not self.backend.offline:
313 aed9b901 Christos Stavrakakis
            return get_client(self.backend_hash, self.backend_id)
314 aed9b901 Christos Stavrakakis
        else:
315 aed9b901 Christos Stavrakakis
            raise ServiceUnavailable
316 aed9b901 Christos Stavrakakis
317 d08a5f6f Vangelis Koukis
    # Error classes
318 d08a5f6f Vangelis Koukis
    class InvalidBackendIdError(Exception):
319 1bce82c0 Christos Stavrakakis
        def __init__(self, value):
320 d08a5f6f Vangelis Koukis
            self.value = value
321 1bce82c0 Christos Stavrakakis
322 1bce82c0 Christos Stavrakakis
        def __str__(self):
323 d08a5f6f Vangelis Koukis
            return repr(self.value)
324 d08a5f6f Vangelis Koukis
325 d08a5f6f Vangelis Koukis
    class InvalidBackendMsgError(Exception):
326 1bce82c0 Christos Stavrakakis
        def __init__(self, opcode, status):
327 d08a5f6f Vangelis Koukis
            self.opcode = opcode
328 d08a5f6f Vangelis Koukis
            self.status = status
329 1bce82c0 Christos Stavrakakis
330 1bce82c0 Christos Stavrakakis
        def __str__(self):
331 9ff7ca6f Giorgos Verigakis
            return repr('<opcode: %s, status: %s>' % (self.opcode,
332 1bce82c0 Christos Stavrakakis
                        self.status))
333 d08a5f6f Vangelis Koukis
334 d08a5f6f Vangelis Koukis
    class InvalidActionError(Exception):
335 1bce82c0 Christos Stavrakakis
        def __init__(self, action):
336 c92af313 Vangelis Koukis
            self._action = action
337 1bce82c0 Christos Stavrakakis
338 1bce82c0 Christos Stavrakakis
        def __str__(self):
339 78d9b6b7 Vangelis Koukis
            return repr(str(self._action))
340 ce55f211 Kostas Papadimitriou
341 5231a38a Giorgos Verigakis
    class DeletedError(Exception):
342 5231a38a Giorgos Verigakis
        pass
343 ce55f211 Kostas Papadimitriou
344 5231a38a Giorgos Verigakis
    class BuildingError(Exception):
345 5231a38a Giorgos Verigakis
        pass
346 ce55f211 Kostas Papadimitriou
347 3846dfd0 Vassilios Karakoidas
    def __init__(self, *args, **kw):
348 d08a5f6f Vangelis Koukis
        """Initialize state for just created VM instances."""
349 d08a5f6f Vangelis Koukis
        super(VirtualMachine, self).__init__(*args, **kw)
350 bc9c872b Vangelis Koukis
        # This gets called BEFORE an instance gets save()d for
351 bc9c872b Vangelis Koukis
        # the first time.
352 ce55f211 Kostas Papadimitriou
        if not self.pk:
353 dfd19c2d Vassilios Karakoidas
            self.action = None
354 dfd19c2d Vassilios Karakoidas
            self.backendjobid = None
355 dfd19c2d Vassilios Karakoidas
            self.backendjobstatus = None
356 dfd19c2d Vassilios Karakoidas
            self.backendopcode = None
357 dfd19c2d Vassilios Karakoidas
            self.backendlogmsg = None
358 dfd19c2d Vassilios Karakoidas
            self.operstate = 'BUILD'
359 ce55f211 Kostas Papadimitriou
360 aed9b901 Christos Stavrakakis
    def save(self, *args, **kwargs):
361 aed9b901 Christos Stavrakakis
        # Store hash for first time saved vm
362 aed9b901 Christos Stavrakakis
        if (self.id is None or self.backend_hash == '') and self.backend:
363 aed9b901 Christos Stavrakakis
            self.backend_hash = self.backend.hash
364 aed9b901 Christos Stavrakakis
        super(VirtualMachine, self).save(*args, **kwargs)
365 aed9b901 Christos Stavrakakis
366 9ff7ca6f Giorgos Verigakis
    @property
367 924d8085 Christos Stavrakakis
    def backend_vm_id(self):
368 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
369 76ba77c1 Vangelis Koukis
        if not self.id:
370 76ba77c1 Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError("self.id is None")
371 9ff7ca6f Giorgos Verigakis
        return '%s%s' % (settings.BACKEND_PREFIX_ID, self.id)
372 ce55f211 Kostas Papadimitriou
373 d08a5f6f Vangelis Koukis
    class Meta:
374 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
375 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
376 ce55f211 Kostas Papadimitriou
377 d08a5f6f Vangelis Koukis
    def __unicode__(self):
378 d08a5f6f Vangelis Koukis
        return self.name
379 d08a5f6f Vangelis Koukis
380 4ffb82dc Vassilios Karakoidas
381 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
382 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
383 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
384 7cc3c7d9 Giorgos Verigakis
    vm = models.ForeignKey(VirtualMachine, related_name='metadata')
385 ce55f211 Kostas Papadimitriou
386 be7b8d37 Vassilios Karakoidas
    class Meta:
387 1bd24df6 Giorgos Verigakis
        unique_together = (('meta_key', 'vm'),)
388 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
389 ce55f211 Kostas Papadimitriou
390 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
391 9ff7ca6f Giorgos Verigakis
        return u'%s: %s' % (self.meta_key, self.meta_value)
392 be7b8d37 Vassilios Karakoidas
393 be7b8d37 Vassilios Karakoidas
394 0269afd6 Giorgos Verigakis
class Network(models.Model):
395 f533f224 Vangelis Koukis
    NETWORK_STATES = (
396 f533f224 Vangelis Koukis
        ('ACTIVE', 'Active'),
397 f533f224 Vangelis Koukis
        ('DELETED', 'Deleted')
398 f533f224 Vangelis Koukis
    )
399 ce55f211 Kostas Papadimitriou
400 0269afd6 Giorgos Verigakis
    name = models.CharField(max_length=255)
401 0269afd6 Giorgos Verigakis
    created = models.DateTimeField(auto_now_add=True)
402 0269afd6 Giorgos Verigakis
    updated = models.DateTimeField(auto_now=True)
403 244c552b Giorgos Verigakis
    userid = models.CharField('User ID of the owner', max_length=100,
404 244c552b Giorgos Verigakis
                              null=True)
405 f533f224 Vangelis Koukis
    state = models.CharField(choices=NETWORK_STATES, max_length=30)
406 f533f224 Vangelis Koukis
    public = models.BooleanField(default=False)
407 f533f224 Vangelis Koukis
    link = models.ForeignKey('NetworkLink', related_name='+')
408 f533f224 Vangelis Koukis
    machines = models.ManyToManyField(VirtualMachine,
409 244c552b Giorgos Verigakis
                                      through='NetworkInterface')
410 ce55f211 Kostas Papadimitriou
411 0269afd6 Giorgos Verigakis
    def __unicode__(self):
412 0269afd6 Giorgos Verigakis
        return self.name
413 4f8b91cd Georgios Gousios
414 f533f224 Vangelis Koukis
415 f533f224 Vangelis Koukis
class NetworkInterface(models.Model):
416 f533f224 Vangelis Koukis
    FIREWALL_PROFILES = (
417 f533f224 Vangelis Koukis
        ('ENABLED', 'Enabled'),
418 26563957 Giorgos Verigakis
        ('DISABLED', 'Disabled'),
419 26563957 Giorgos Verigakis
        ('PROTECTED', 'Protected')
420 f533f224 Vangelis Koukis
    )
421 ce55f211 Kostas Papadimitriou
422 f533f224 Vangelis Koukis
    machine = models.ForeignKey(VirtualMachine, related_name='nics')
423 f533f224 Vangelis Koukis
    network = models.ForeignKey(Network, related_name='nics')
424 f533f224 Vangelis Koukis
    created = models.DateTimeField(auto_now_add=True)
425 f533f224 Vangelis Koukis
    updated = models.DateTimeField(auto_now=True)
426 f533f224 Vangelis Koukis
    index = models.IntegerField(null=True)
427 f533f224 Vangelis Koukis
    mac = models.CharField(max_length=17, null=True)
428 f533f224 Vangelis Koukis
    ipv4 = models.CharField(max_length=15, null=True)
429 f533f224 Vangelis Koukis
    ipv6 = models.CharField(max_length=100, null=True)
430 f533f224 Vangelis Koukis
    firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
431 244c552b Giorgos Verigakis
                                        max_length=30, null=True)
432 ce55f211 Kostas Papadimitriou
433 2987cd8a Giorgos Verigakis
    def __unicode__(self):
434 2987cd8a Giorgos Verigakis
        return '%s@%s' % (self.machine.name, self.network.name)
435 f533f224 Vangelis Koukis
436 f533f224 Vangelis Koukis
437 f533f224 Vangelis Koukis
class NetworkLink(models.Model):
438 f533f224 Vangelis Koukis
    network = models.ForeignKey(Network, null=True, related_name='+')
439 f533f224 Vangelis Koukis
    index = models.IntegerField()
440 f533f224 Vangelis Koukis
    name = models.CharField(max_length=255)
441 f533f224 Vangelis Koukis
    available = models.BooleanField(default=True)
442 ce55f211 Kostas Papadimitriou
443 2987cd8a Giorgos Verigakis
    def __unicode__(self):
444 2987cd8a Giorgos Verigakis
        return self.name
445 ce55f211 Kostas Papadimitriou
446 ce55f211 Kostas Papadimitriou
    class NotAvailable(Exception):
447 ce55f211 Kostas Papadimitriou
        pass