Statistics
| Branch: | Tag: | Revision:

root / db / models.py @ 52194743

History | View | Annotate | Download (15.8 kB)

1 46547e14 Faidon Liambotis
# vim: ts=4 sts=4 et ai sw=4 fileencoding=utf-8
2 46547e14 Faidon Liambotis
3 9dbe70f8 Markos Gogoulos
from django.conf import settings
4 17935f41 Vassilios Karakoidas
from django.db import models
5 78dec216 Vassilios Karakoidas
from django.contrib.auth.models import User
6 38684389 Markos Gogoulos
from django.utils.translation import gettext_lazy as _
7 9dbe70f8 Markos Gogoulos
8 38684389 Markos Gogoulos
import datetime
9 38684389 Markos Gogoulos
10 f5ad2956 Markos Gogoulos
backend_prefix_id = settings.BACKEND_PREFIX_ID
11 f5ad2956 Markos Gogoulos
12 d08a5f6f Vangelis Koukis
class SynnefoUser(models.Model):
13 46547e14 Faidon Liambotis
    name = models.CharField(max_length=255)
14 46547e14 Faidon Liambotis
    credit = models.IntegerField()
15 52194743 Vangelis Koukis
    created = models.DateTimeField('Time of creation', auto_now_add=True)
16 52194743 Vangelis Koukis
    updated = models.DateTimeField('Time of last update', auto_now=True)
17 3f04688d Vassilios Karakoidas
    monthly_rate = models.IntegerField()
18 6496c622 Vassilios Karakoidas
    user = models.ForeignKey(User)
19 37c42c95 Vassilios Karakoidas
    violations = models.IntegerField()
20 be7b8d37 Vassilios Karakoidas
    
21 be7b8d37 Vassilios Karakoidas
    class Meta:
22 d08a5f6f Vangelis Koukis
        verbose_name = u'Synnefo User'
23 be7b8d37 Vassilios Karakoidas
    
24 46547e14 Faidon Liambotis
    def __unicode__(self):
25 46547e14 Faidon Liambotis
        return self.name
26 d8e682ed Vassilios Karakoidas
    
27 61b80d9a Vassilios Karakoidas
    def charge_credits(self, cost, start, end):
28 0e1fbceb Vassilios Karakoidas
        """Reduce user credits for specified duration.
29 0e1fbceb Vassilios Karakoidas
        
30 0e1fbceb Vassilios Karakoidas
        Returns amount of credits remaining. Negative if the user has surpassed his limit.
31 0e1fbceb Vassilios Karakoidas
        
32 0e1fbceb Vassilios Karakoidas
        """
33 c5fb1b95 Vassilios Karakoidas
        td = end - start
34 c5fb1b95 Vassilios Karakoidas
        sec = float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6)
35 c5fb1b95 Vassilios Karakoidas
        
36 c5fb1b95 Vassilios Karakoidas
        total_hours = float(sec) / float(60.0*60.0)
37 c5fb1b95 Vassilios Karakoidas
        total_cost = float(cost)*total_hours
38 61b80d9a Vassilios Karakoidas
        
39 61b80d9a Vassilios Karakoidas
        self.credit = self.credit - round(total_cost)
40 37c42c95 Vassilios Karakoidas
        
41 37c42c95 Vassilios Karakoidas
        if self.credit < 0:
42 37c42c95 Vassilios Karakoidas
            self.violations = self.violations + 1
43 37c42c95 Vassilios Karakoidas
        else:
44 37c42c95 Vassilios Karakoidas
            self.violations = 0
45 d8e682ed Vassilios Karakoidas
                
46 91509aed Vassilios Karakoidas
        return self.credit
47 d8e682ed Vassilios Karakoidas
    
48 b90da243 Vassilios Karakoidas
    def allocate_credits(self):
49 378bf5b7 Vangelis Koukis
        """Allocate credits. Add monthly rate to user credit reserve."""
50 fa672379 Vassilios Karakoidas
        self.credit = self.credit + self.monthly_rate
51 fa672379 Vassilios Karakoidas
        
52 fcbc5bb3 Vassilios Karakoidas
        # ensure that the user has not more credits than his quota
53 1583f11a Vassilios Karakoidas
        limit_quota = self.get_limit('QUOTA_CREDIT')
54 90b28517 Vassilios Karakoidas
                
55 90b28517 Vassilios Karakoidas
        if self.credit > limit_quota:
56 90b28517 Vassilios Karakoidas
            self.credit = limit_quota
57 07f3219d Vassilios Karakoidas
58 1583f11a Vassilios Karakoidas
    def get_limit(self, limit_name):
59 1583f11a Vassilios Karakoidas
        """Returns the limit value for the specified limit"""
60 1583f11a Vassilios Karakoidas
        limit_objs = Limit.objects.filter(name=limit_name, user=self)
61 1583f11a Vassilios Karakoidas
        
62 1583f11a Vassilios Karakoidas
        if len(limit_objs) == 1:
63 1583f11a Vassilios Karakoidas
            return limit_objs[0].value
64 1583f11a Vassilios Karakoidas
        
65 1583f11a Vassilios Karakoidas
        return 0
66 1583f11a Vassilios Karakoidas
        
67 0b5a51f7 Vassilios Karakoidas
68 6496c622 Vassilios Karakoidas
class Image(models.Model):
69 6496c622 Vassilios Karakoidas
    # This is WIP, FIXME
70 6496c622 Vassilios Karakoidas
    IMAGE_STATES = (
71 6496c622 Vassilios Karakoidas
                ('ACTIVE', 'Active'),
72 6496c622 Vassilios Karakoidas
                ('SAVING', 'Saving'),
73 6496c622 Vassilios Karakoidas
                ('DELETED', 'Deleted')
74 6496c622 Vassilios Karakoidas
    )
75 6496c622 Vassilios Karakoidas
76 6496c622 Vassilios Karakoidas
    name = models.CharField(max_length=255, help_text=_('description'))
77 6496c622 Vassilios Karakoidas
    state = models.CharField(choices=IMAGE_STATES, max_length=30)
78 6496c622 Vassilios Karakoidas
    description = models.TextField(help_text=_('description'))
79 6496c622 Vassilios Karakoidas
    owner = models.ForeignKey(SynnefoUser,blank=True, null=True)
80 52194743 Vangelis Koukis
    created = models.DateTimeField('Time of creation', auto_now_add=True)
81 52194743 Vangelis Koukis
    updated = models.DateTimeField('Time of last update', auto_now=True)
82 6496c622 Vassilios Karakoidas
83 6496c622 Vassilios Karakoidas
    class Meta:
84 6496c622 Vassilios Karakoidas
        verbose_name = u'Image'
85 6496c622 Vassilios Karakoidas
86 6496c622 Vassilios Karakoidas
    def __unicode__(self):
87 6496c622 Vassilios Karakoidas
        return u'%s' % (self.name)
88 6496c622 Vassilios Karakoidas
89 00d83c42 Markos Gogoulos
    def get_vmid(self):
90 f1220c03 Markos Gogoulos
        """Returns first Virtual Machine's id, if any
91 f1220c03 Markos Gogoulos
           an Image might be the ForeignKey to one or many VirtualMachines
92 f1220c03 Markos Gogoulos
           we want the vm that created the Image (the first one)
93 f1220c03 Markos Gogoulos
        """
94 00d83c42 Markos Gogoulos
        if self.virtualmachine_set.all():
95 00d83c42 Markos Gogoulos
            return self.virtualmachine_set.all()[0].id
96 00d83c42 Markos Gogoulos
        else:
97 00d83c42 Markos Gogoulos
            return ''
98 00d83c42 Markos Gogoulos
99 00d83c42 Markos Gogoulos
    vm_id = property(get_vmid)
100 6496c622 Vassilios Karakoidas
101 53827f6d Markos Gogoulos
102 53827f6d Markos Gogoulos
class ImageMetadata(models.Model):
103 53827f6d Markos Gogoulos
    meta_key = models.CharField(max_length=50)
104 53827f6d Markos Gogoulos
    meta_value = models.CharField(max_length=500)
105 53827f6d Markos Gogoulos
    image = models.ForeignKey(Image)
106 53827f6d Markos Gogoulos
    
107 53827f6d Markos Gogoulos
    class Meta:
108 53827f6d Markos Gogoulos
        verbose_name = u'Key-value pair of metadata for an Image.'
109 53827f6d Markos Gogoulos
    
110 53827f6d Markos Gogoulos
    def __unicode__(self):
111 53827f6d Markos Gogoulos
        return u'%s, %s for %s' % (self.meta_key, self.meta_value, self.image.name)
112 53827f6d Markos Gogoulos
113 53827f6d Markos Gogoulos
114 f51e7b50 Vassilios Karakoidas
class Limit(models.Model):
115 2d45374d Vassilios Karakoidas
    LIMITS = (
116 2d45374d Vassilios Karakoidas
        ('QUOTA_CREDIT', 'Maximum number of credits per user'),
117 2d45374d Vassilios Karakoidas
        ('MAX_VIOLATIONS', 'Maximum number of credit violation per user')
118 2d45374d Vassilios Karakoidas
    )
119 d08a5f6f Vangelis Koukis
    user = models.ForeignKey(SynnefoUser)
120 90b28517 Vassilios Karakoidas
    name = models.CharField(choices=LIMITS, max_length=30, null=False)
121 46547e14 Faidon Liambotis
    value = models.IntegerField()
122 be7b8d37 Vassilios Karakoidas
    
123 46547e14 Faidon Liambotis
    class Meta:
124 dcfc6c2d Vangelis Koukis
        verbose_name = u'Enforced limit for user'
125 be7b8d37 Vassilios Karakoidas
    
126 46547e14 Faidon Liambotis
    def __unicode__(self):
127 46547e14 Faidon Liambotis
        return u'Limit %s for user %s: %d' % (self.limit, self.user, self.value)
128 46547e14 Faidon Liambotis
129 e7164ebc Vassilios Karakoidas
130 e7164ebc Vassilios Karakoidas
class Flavor(models.Model):
131 be7b8d37 Vassilios Karakoidas
    cpu = models.IntegerField(default=0)
132 be7b8d37 Vassilios Karakoidas
    ram = models.IntegerField(default=0)
133 be7b8d37 Vassilios Karakoidas
    disk = models.IntegerField(default=0)
134 be7b8d37 Vassilios Karakoidas
    
135 be7b8d37 Vassilios Karakoidas
    class Meta:
136 dcfc6c2d Vangelis Koukis
        verbose_name = u'Virtual machine flavor'
137 6c9a62b5 Vassilios Karakoidas
            
138 be7b8d37 Vassilios Karakoidas
    def _get_name(self):
139 f0f4edac Vassilios Karakoidas
        """Returns flavor name (generated)"""
140 d08a5f6f Vangelis Koukis
        return u'C%dR%dD%d' % (self.cpu, self.ram, self.disk)
141 46547e14 Faidon Liambotis
142 05a90a5b Vassilios Karakoidas
    def _get_cost_inactive(self):
143 f0f4edac Vassilios Karakoidas
        """Returns the inactive cost for a Flavor (usually only disk usage counts)"""
144 05a90a5b Vassilios Karakoidas
        self._update_costs()
145 e8fa4283 Vassilios Karakoidas
        return self._cost_inactive
146 05a90a5b Vassilios Karakoidas
147 05a90a5b Vassilios Karakoidas
    def _get_cost_active(self):
148 f0f4edac Vassilios Karakoidas
        """Returns the active cost for a Flavor"""
149 05a90a5b Vassilios Karakoidas
        self._update_costs()
150 e8fa4283 Vassilios Karakoidas
        return self._cost_active
151 05a90a5b Vassilios Karakoidas
    
152 05a90a5b Vassilios Karakoidas
    def _update_costs(self):
153 0e1fbceb Vassilios Karakoidas
        """Update the internal cost_active, cost_inactive variables"""
154 0e1fbceb Vassilios Karakoidas
        if not hasattr(self, '_cost_active'):
155 3846dfd0 Vassilios Karakoidas
            fch_list = FlavorCostHistory.objects.filter(flavor=self).order_by('-effective_from')
156 3846dfd0 Vassilios Karakoidas
            if len(fch_list) > 0:
157 3846dfd0 Vassilios Karakoidas
                fch = fch_list[0]
158 3846dfd0 Vassilios Karakoidas
                self._cost_active = fch.cost_active
159 3846dfd0 Vassilios Karakoidas
                self._cost_inactive = fch.cost_inactive
160 3846dfd0 Vassilios Karakoidas
            else:
161 3846dfd0 Vassilios Karakoidas
                self._cost_active = 0
162 3846dfd0 Vassilios Karakoidas
                self._cost_inactive = 0
163 05a90a5b Vassilios Karakoidas
164 be7b8d37 Vassilios Karakoidas
    name = property(_get_name)
165 05a90a5b Vassilios Karakoidas
    cost_active = property(_get_cost_active)
166 05a90a5b Vassilios Karakoidas
    cost_inactive = property(_get_cost_inactive)
167 00318048 Vassilios Karakoidas
168 46547e14 Faidon Liambotis
    def __unicode__(self):
169 46547e14 Faidon Liambotis
        return self.name
170 3846dfd0 Vassilios Karakoidas
    
171 3846dfd0 Vassilios Karakoidas
    def get_price_list(self):
172 f0f4edac Vassilios Karakoidas
        """Returns the price catalog for this Flavor"""
173 5cb8d769 Vassilios Karakoidas
        fch_list = FlavorCostHistory.objects.filter(flavor=self).order_by('effective_from')
174 3846dfd0 Vassilios Karakoidas
        
175 8bda0440 Vassilios Karakoidas
        return fch_list
176 8bda0440 Vassilios Karakoidas
        
177 8bda0440 Vassilios Karakoidas
    def find_cost(self, the_date):
178 43d8f0e4 Vassilios Karakoidas
        """Returns costs (FlavorCostHistory instance) for the specified date (the_date)"""
179 8bda0440 Vassilios Karakoidas
        rdate = None
180 8bda0440 Vassilios Karakoidas
        fch_list = self.get_price_list()
181 8bda0440 Vassilios Karakoidas
        
182 8bda0440 Vassilios Karakoidas
        for fc in fch_list:
183 8bda0440 Vassilios Karakoidas
            if the_date > fc.effective_from:
184 8bda0440 Vassilios Karakoidas
                rdate = fc
185 8bda0440 Vassilios Karakoidas
            else:
186 8bda0440 Vassilios Karakoidas
                break
187 8bda0440 Vassilios Karakoidas
        
188 8bda0440 Vassilios Karakoidas
        return rdate
189 46547e14 Faidon Liambotis
190 46547e14 Faidon Liambotis
191 41b972e6 Vassilios Karakoidas
class FlavorCostHistory(models.Model):
192 41b972e6 Vassilios Karakoidas
    cost_active = models.PositiveIntegerField()
193 41b972e6 Vassilios Karakoidas
    cost_inactive = models.PositiveIntegerField()
194 41b972e6 Vassilios Karakoidas
    effective_from = models.DateField()
195 41b972e6 Vassilios Karakoidas
    flavor = models.ForeignKey(Flavor)
196 41b972e6 Vassilios Karakoidas
    
197 41b972e6 Vassilios Karakoidas
    class Meta:
198 41b972e6 Vassilios Karakoidas
        verbose_name = u'Pricing history for flavors'
199 41b972e6 Vassilios Karakoidas
    
200 41b972e6 Vassilios Karakoidas
    def __unicode__(self):
201 3846dfd0 Vassilios Karakoidas
        return u'Costs (up, down)=(%d, %d) for %s since %s' % (self.cost_active, self.cost_inactive, flavor.name, self.effective_from)
202 41b972e6 Vassilios Karakoidas
203 41b972e6 Vassilios Karakoidas
204 07f3219d Vassilios Karakoidas
class VirtualMachine(models.Model):
205 d08a5f6f Vangelis Koukis
    ACTIONS = (
206 d08a5f6f Vangelis Koukis
       ('CREATE', 'Create VM'),
207 d08a5f6f Vangelis Koukis
       ('START', 'Start VM'),
208 d08a5f6f Vangelis Koukis
       ('STOP', 'Shutdown VM'),
209 d08a5f6f Vangelis Koukis
       ('SUSPEND', 'Admin Suspend VM'),
210 d08a5f6f Vangelis Koukis
       ('REBOOT', 'Reboot VM'),
211 d08a5f6f Vangelis Koukis
       ('DESTROY', 'Destroy VM')
212 d08a5f6f Vangelis Koukis
    )
213 d08a5f6f Vangelis Koukis
214 d08a5f6f Vangelis Koukis
    OPER_STATES = (
215 d08a5f6f Vangelis Koukis
        ('BUILD', 'Queued for creation'),
216 d08a5f6f Vangelis Koukis
        ('ERROR', 'Creation failed'),
217 d08a5f6f Vangelis Koukis
        ('STOPPED', 'Stopped'),
218 d08a5f6f Vangelis Koukis
        ('STARTED', 'Started'),
219 d08a5f6f Vangelis Koukis
        ('DESTROYED', 'Destroyed')
220 d08a5f6f Vangelis Koukis
    )
221 d08a5f6f Vangelis Koukis
222 d08a5f6f Vangelis Koukis
    BACKEND_OPCODES = (
223 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_CREATE', 'Create Instance'),
224 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_REMOVE', 'Remove Instance'),
225 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_STARTUP', 'Startup Instance'),
226 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_SHUTDOWN', 'Shutdown Instance'),
227 d08a5f6f Vangelis Koukis
        ('OP_INSTANCE_REBOOT', 'Reboot Instance')
228 d08a5f6f Vangelis Koukis
    )
229 d08a5f6f Vangelis Koukis
230 d08a5f6f Vangelis Koukis
    BACKEND_STATUSES = (
231 d08a5f6f Vangelis Koukis
        ('queued', 'request queued'),
232 d08a5f6f Vangelis Koukis
        ('waiting', 'request waiting for locks'),
233 d08a5f6f Vangelis Koukis
        ('canceling', 'request being canceled'),
234 d08a5f6f Vangelis Koukis
        ('running', 'request running'),
235 d08a5f6f Vangelis Koukis
        ('canceled', 'request canceled'),
236 d08a5f6f Vangelis Koukis
        ('success', 'request completed successfully'),
237 d08a5f6f Vangelis Koukis
        ('error', 'request returned error')
238 d08a5f6f Vangelis Koukis
    )
239 d08a5f6f Vangelis Koukis
240 d08a5f6f Vangelis Koukis
    # The operating state of a VM,
241 d08a5f6f Vangelis Koukis
    # upon the successful completion of a backend operation.
242 d08a5f6f Vangelis Koukis
    OPER_STATE_FROM_OPCODE = {
243 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_CREATE': 'STARTED',
244 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_REMOVE': 'DESTROYED',
245 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_STARTUP': 'STARTED',
246 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_SHUTDOWN': 'STOPPED',
247 d08a5f6f Vangelis Koukis
        'OP_INSTANCE_REBOOT': 'STARTED'
248 d08a5f6f Vangelis Koukis
    }
249 d08a5f6f Vangelis Koukis
250 d08a5f6f Vangelis Koukis
    RSAPI_STATE_FROM_OPER_STATE = {
251 d08a5f6f Vangelis Koukis
        "BUILD": "BUILD",
252 d08a5f6f Vangelis Koukis
        "ERROR": "ERROR",
253 d08a5f6f Vangelis Koukis
        "STOPPED": "STOPPED",
254 d08a5f6f Vangelis Koukis
        "STARTED": "ACTIVE",
255 d08a5f6f Vangelis Koukis
        "DESTROYED": "DELETED"
256 d08a5f6f Vangelis Koukis
    }
257 d08a5f6f Vangelis Koukis
258 46547e14 Faidon Liambotis
    name = models.CharField(max_length=255)
259 52194743 Vangelis Koukis
    owner = models.ForeignKey(SynnefoUser,blank=True, null=True)
260 52194743 Vangelis Koukis
    created = models.DateTimeField('Time of creation', auto_now_add=True)
261 52194743 Vangelis Koukis
    updated = models.DateTimeField('Time of last update', auto_now=True)
262 52194743 Vangelis Koukis
    charged = models.DateTimeField('Time of last charge', default=datetime.datetime.now())
263 d08a5f6f Vangelis Koukis
    # Use string reference to avoid circular ForeignKey def.
264 d08a5f6f Vangelis Koukis
    # FIXME: "sourceimage" works, "image" causes validation errors. See "related_name" in the Django docs.
265 9373e677 Vassilios Karakoidas
    sourceimage = models.ForeignKey(Image, null=False) 
266 22571df6 Vassilios Karakoidas
    hostid = models.CharField(max_length=100)
267 d08a5f6f Vangelis Koukis
    description = models.TextField(help_text=_('description'))
268 be7b8d37 Vassilios Karakoidas
    ipfour = models.IPAddressField()
269 be7b8d37 Vassilios Karakoidas
    ipsix = models.CharField(max_length=100)
270 46547e14 Faidon Liambotis
    flavor = models.ForeignKey(Flavor)
271 d08a5f6f Vangelis Koukis
    suspended = models.BooleanField('Administratively Suspended')
272 d08a5f6f Vangelis Koukis
273 52194743 Vangelis Koukis
    # VM State 
274 52194743 Vangelis Koukis
    # The following fields are volatile data, in the sense
275 52194743 Vangelis Koukis
    # that they need not be persistent in the DB, but rather
276 52194743 Vangelis Koukis
    # get generated at runtime by quering Ganeti and applying
277 52194743 Vangelis Koukis
    # updates received from Ganeti.
278 52194743 Vangelis Koukis
    #
279 52194743 Vangelis Koukis
    # They belong to a separate caching layer, in the long run.
280 52194743 Vangelis Koukis
    # [vkoukis] after discussion with [faidon].
281 d08a5f6f Vangelis Koukis
    action = models.CharField(choices=ACTIONS, max_length=30, null=True)
282 78d9b6b7 Vangelis Koukis
    _operstate = models.CharField(choices=OPER_STATES, max_length=30, null=True)
283 78d9b6b7 Vangelis Koukis
    _backendjobid = models.PositiveIntegerField(null=True)
284 78d9b6b7 Vangelis Koukis
    _backendopcode = models.CharField(choices=BACKEND_OPCODES, max_length=30, null=True)
285 78d9b6b7 Vangelis Koukis
    _backendjobstatus = models.CharField(choices=BACKEND_STATUSES, max_length=30, null=True)
286 78d9b6b7 Vangelis Koukis
    _backendlogmsg = models.TextField(null=True)
287 d08a5f6f Vangelis Koukis
288 d08a5f6f Vangelis Koukis
    # Error classes
289 d08a5f6f Vangelis Koukis
    class InvalidBackendIdError(Exception):
290 d08a5f6f Vangelis Koukis
         def __init__(self, value):
291 d08a5f6f Vangelis Koukis
            self.value = value
292 d08a5f6f Vangelis Koukis
         def __str__(self):
293 d08a5f6f Vangelis Koukis
            return repr(self.value)
294 d08a5f6f Vangelis Koukis
295 d08a5f6f Vangelis Koukis
    class InvalidBackendMsgError(Exception):
296 d08a5f6f Vangelis Koukis
         def __init__(self, opcode, status):
297 d08a5f6f Vangelis Koukis
            self.opcode = opcode
298 d08a5f6f Vangelis Koukis
            self.status = status
299 d08a5f6f Vangelis Koukis
         def __str__(self):
300 d08a5f6f Vangelis Koukis
            return repr("<opcode: %s, status: %s>" % (str(self.opcode), str(self.status)))
301 d08a5f6f Vangelis Koukis
302 d08a5f6f Vangelis Koukis
    class InvalidActionError(Exception):
303 d08a5f6f Vangelis Koukis
         def __init__(self, action):
304 78d9b6b7 Vangelis Koukis
            self.__action = action
305 d08a5f6f Vangelis Koukis
         def __str__(self):
306 78d9b6b7 Vangelis Koukis
            return repr(str(self._action))
307 d08a5f6f Vangelis Koukis
308 d08a5f6f Vangelis Koukis
    @staticmethod
309 d08a5f6f Vangelis Koukis
    def id_from_instance_name(name):
310 d08a5f6f Vangelis Koukis
        """Returns VirtualMachine's Django id, given a ganeti machine name.
311 d08a5f6f Vangelis Koukis

312 d08a5f6f Vangelis Koukis
        Strips the ganeti prefix atm. Needs a better name!
313 d08a5f6f Vangelis Koukis
        
314 d08a5f6f Vangelis Koukis
        """
315 d08a5f6f Vangelis Koukis
        if not str(name).startswith(backend_prefix_id):
316 d08a5f6f Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError(str(name))
317 d08a5f6f Vangelis Koukis
        ns = str(name).lstrip(backend_prefix_id)
318 d08a5f6f Vangelis Koukis
        if not ns.isdigit():
319 d08a5f6f Vangelis Koukis
            raise VirtualMachine.InvalidBackendIdError(str(name))
320 d08a5f6f Vangelis Koukis
        return int(ns)
321 d08a5f6f Vangelis Koukis
322 3846dfd0 Vassilios Karakoidas
    def __init__(self, *args, **kw):
323 d08a5f6f Vangelis Koukis
        """Initialize state for just created VM instances."""
324 d08a5f6f Vangelis Koukis
        super(VirtualMachine, self).__init__(*args, **kw)
325 d08a5f6f Vangelis Koukis
        # Before this instance gets save()d
326 d08a5f6f Vangelis Koukis
        if not self.pk: 
327 78d9b6b7 Vangelis Koukis
            self._action = None
328 78d9b6b7 Vangelis Koukis
            self._operstate = "BUILD"
329 78d9b6b7 Vangelis Koukis
            self._backendjobid = None
330 78d9b6b7 Vangelis Koukis
            self._backendjobstatus = None
331 78d9b6b7 Vangelis Koukis
            self._backendopcode = None
332 78d9b6b7 Vangelis Koukis
            self._backendlogmsg = None
333 d08a5f6f Vangelis Koukis
334 d08a5f6f Vangelis Koukis
    def process_backend_msg(self, jobid, opcode, status, logmsg):
335 d08a5f6f Vangelis Koukis
        """Process a job progress notification from the backend.
336 d08a5f6f Vangelis Koukis

337 d08a5f6f Vangelis Koukis
        Process an incoming message from the backend (currently Ganeti).
338 d08a5f6f Vangelis Koukis
        Job notifications with a terminating status (sucess, error, or canceled),
339 d08a5f6f Vangelis Koukis
        also update the operating state of the VM.
340 d08a5f6f Vangelis Koukis

341 d08a5f6f Vangelis Koukis
        """
342 d08a5f6f Vangelis Koukis
        if (opcode not in [x[0] for x in VirtualMachine.BACKEND_OPCODES] or
343 d08a5f6f Vangelis Koukis
           status not in [x[0] for x in VirtualMachine.BACKEND_STATUSES]):
344 d08a5f6f Vangelis Koukis
            raise VirtualMachine.InvalidBackendMsgError(opcode, status)
345 d08a5f6f Vangelis Koukis
346 78d9b6b7 Vangelis Koukis
        self._backendjobid = jobid
347 78d9b6b7 Vangelis Koukis
        self._backendjobstatus = status
348 78d9b6b7 Vangelis Koukis
        self._backendopcode = opcode
349 78d9b6b7 Vangelis Koukis
        self._backendlogmsg = logmsg
350 d08a5f6f Vangelis Koukis
351 d08a5f6f Vangelis Koukis
        # Notifications of success change the operating state
352 d08a5f6f Vangelis Koukis
        if status == 'success':
353 78d9b6b7 Vangelis Koukis
            self._operstate = VirtualMachine.OPER_STATE_FROM_OPCODE[opcode]
354 d08a5f6f Vangelis Koukis
        # Special cases OP_INSTANCE_CREATE fails --> ERROR
355 d08a5f6f Vangelis Koukis
        if status in ('canceled', 'error') and opcode == 'OP_INSTANCE_CREATE':
356 78d9b6b7 Vangelis Koukis
            self._operstate = 'ERROR'
357 d08a5f6f Vangelis Koukis
        # Any other notification of failure leaves the operating state unchanged
358 d08a5f6f Vangelis Koukis
359 d08a5f6f Vangelis Koukis
        self.save()
360 d08a5f6f Vangelis Koukis
361 d08a5f6f Vangelis Koukis
    def start_action(self, action):
362 d08a5f6f Vangelis Koukis
        """Update the state of a VM when a new action is initiated."""
363 d08a5f6f Vangelis Koukis
        if not action in [x[0] for x in VirtualMachine.ACTIONS]:
364 d08a5f6f Vangelis Koukis
            raise VirtualMachine.InvalidActionError(action)
365 d08a5f6f Vangelis Koukis
366 78d9b6b7 Vangelis Koukis
        self._action = action
367 78d9b6b7 Vangelis Koukis
        self._backendjobid = None
368 78d9b6b7 Vangelis Koukis
        self._backendopcode = None
369 78d9b6b7 Vangelis Koukis
        self._backendlogmsg = None
370 52194743 Vangelis Koukis
        
371 d08a5f6f Vangelis Koukis
        self.save()
372 d08a5f6f Vangelis Koukis
373 d08a5f6f Vangelis Koukis
    # FIXME: Perhaps move somewhere else, outside the model?
374 d08a5f6f Vangelis Koukis
    def _get_rsapi_state(self):
375 d08a5f6f Vangelis Koukis
        try:
376 78d9b6b7 Vangelis Koukis
            return VirtualMachine.RSAPI_STATE_FROM_OPER_STATE[self._operstate]
377 d08a5f6f Vangelis Koukis
        except KeyError:
378 d08a5f6f Vangelis Koukis
            return "UNKNOWN"
379 d08a5f6f Vangelis Koukis
380 d08a5f6f Vangelis Koukis
    rsapi_state = property(_get_rsapi_state)
381 7cfb3877 Vassilios Karakoidas
382 224b5e19 Markos Gogoulos
    def _get_backend_id(self):
383 224b5e19 Markos Gogoulos
        """Returns the backend id for this VM by prepending backend-prefix."""
384 f5ad2956 Markos Gogoulos
        return '%s%s' % (backend_prefix_id, str(self.id))
385 5b32775a Vassilios Karakoidas
386 224b5e19 Markos Gogoulos
    backend_id = property(_get_backend_id)
387 05a90a5b Vassilios Karakoidas
388 d08a5f6f Vangelis Koukis
    class Meta:
389 d08a5f6f Vangelis Koukis
        verbose_name = u'Virtual machine instance'
390 d08a5f6f Vangelis Koukis
        get_latest_by = 'created'
391 d08a5f6f Vangelis Koukis
    
392 d08a5f6f Vangelis Koukis
    def __unicode__(self):
393 d08a5f6f Vangelis Koukis
        return self.name
394 d08a5f6f Vangelis Koukis
395 62c6d260 Vassilios Karakoidas
    def get_accounting_logs(self):
396 62c6d260 Vassilios Karakoidas
        """Returns all AcountingLog records after the charged field"""
397 62c6d260 Vassilios Karakoidas
        acc_logs = AccountingLog.objects.filter(date__gte=self.charged, vm=self)
398 62c6d260 Vassilios Karakoidas
        if len(acc_logs) == 0:
399 62c6d260 Vassilios Karakoidas
            return []
400 62c6d260 Vassilios Karakoidas
            
401 62c6d260 Vassilios Karakoidas
        return acc_logs
402 62c6d260 Vassilios Karakoidas
403 05a90a5b Vassilios Karakoidas
404 a31ff6cb Markos Gogoulos
class VirtualMachineGroup(models.Model):
405 52194743 Vangelis Koukis
    """Groups of VMs for SynnefoUsers"""
406 a31ff6cb Markos Gogoulos
    name = models.CharField(max_length=255)
407 52194743 Vangelis Koukis
    created = models.DateTimeField('Time of creation', auto_now_add=True)
408 52194743 Vangelis Koukis
    updated = models.DateTimeField('Time of last update', auto_now=True)
409 6496c622 Vassilios Karakoidas
    owner = models.ForeignKey(SynnefoUser)
410 a31ff6cb Markos Gogoulos
    machines = models.ManyToManyField(VirtualMachine)
411 a31ff6cb Markos Gogoulos
412 a31ff6cb Markos Gogoulos
    class Meta:
413 a31ff6cb Markos Gogoulos
        verbose_name = u'Virtual Machine Group'
414 a31ff6cb Markos Gogoulos
        verbose_name_plural = 'Virtual Machine Groups'
415 a31ff6cb Markos Gogoulos
        ordering = ['name']
416 a31ff6cb Markos Gogoulos
    
417 a31ff6cb Markos Gogoulos
    def __unicode__(self):
418 a31ff6cb Markos Gogoulos
        return self.name
419 a31ff6cb Markos Gogoulos
420 3f53219b Vassilios Karakoidas
421 be7b8d37 Vassilios Karakoidas
class VirtualMachineMetadata(models.Model):
422 be7b8d37 Vassilios Karakoidas
    meta_key = models.CharField(max_length=50)
423 be7b8d37 Vassilios Karakoidas
    meta_value = models.CharField(max_length=500)
424 be7b8d37 Vassilios Karakoidas
    vm = models.ForeignKey(VirtualMachine)
425 be7b8d37 Vassilios Karakoidas
    
426 be7b8d37 Vassilios Karakoidas
    class Meta:
427 dcfc6c2d Vangelis Koukis
        verbose_name = u'Key-value pair of metadata for a VM.'
428 be7b8d37 Vassilios Karakoidas
    
429 be7b8d37 Vassilios Karakoidas
    def __unicode__(self):
430 53827f6d Markos Gogoulos
        return u'%s, %s for %s' % (self.meta_key, self.meta_value, self.vm.name)
431 be7b8d37 Vassilios Karakoidas
432 be7b8d37 Vassilios Karakoidas
433 a09c1220 Vassilios Karakoidas
class AccountingLog(models.Model):
434 46547e14 Faidon Liambotis
    vm = models.ForeignKey(VirtualMachine)
435 46547e14 Faidon Liambotis
    date = models.DateTimeField()
436 d08a5f6f Vangelis Koukis
    state = models.CharField(choices=VirtualMachine.OPER_STATES, max_length=30)
437 be7b8d37 Vassilios Karakoidas
    
438 46547e14 Faidon Liambotis
    class Meta:
439 b90da243 Vassilios Karakoidas
        verbose_name = u'Accounting log'
440 be7b8d37 Vassilios Karakoidas
441 5b32775a Vassilios Karakoidas
    def __unicode__(self):
442 9373e677 Vassilios Karakoidas
        return u'%s - %s - %s' % (self.vm.name, str(self.date), self.state)
443 b86e97ed Vassilios Karakoidas
    
444 b86e97ed Vassilios Karakoidas
    @staticmethod   
445 b86e97ed Vassilios Karakoidas
    def get_log_entries(vm_obj, date_from):
446 9373e677 Vassilios Karakoidas
        """Returns log entries for the specified vm after a date"""
447 9373e677 Vassilios Karakoidas
        entries = AccountingLog.objects.filter(vm=vm_obj).filter(date__gte=date_from).order_by('-date')
448 9071888e Constantinos Venetsanopoulos
    
449 9373e677 Vassilios Karakoidas
        return entries
450 38684389 Markos Gogoulos
451 9071888e Constantinos Venetsanopoulos
452 9071888e Constantinos Venetsanopoulos
class Disk(models.Model):
453 9071888e Constantinos Venetsanopoulos
    name = models.CharField(max_length=255)
454 52194743 Vangelis Koukis
    created = models.DateTimeField('Time of creation', auto_now_add=True)
455 52194743 Vangelis Koukis
    updated = models.DateTimeField('Time of last update', auto_now=True)
456 9071888e Constantinos Venetsanopoulos
    size = models.PositiveIntegerField('Disk size in GBs')
457 9071888e Constantinos Venetsanopoulos
    vm = models.ForeignKey(VirtualMachine, blank=True, null=True)
458 9071888e Constantinos Venetsanopoulos
    owner = models.ForeignKey(SynnefoUser, blank=True, null=True)  
459 52194743 Vangelis Koukis
460 9071888e Constantinos Venetsanopoulos
    class Meta:
461 9071888e Constantinos Venetsanopoulos
        verbose_name = u'Disk instance'
462 9071888e Constantinos Venetsanopoulos
463 9071888e Constantinos Venetsanopoulos
    def __unicode__(self):
464 9071888e Constantinos Venetsanopoulos
        return self.name