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 |