root / snf-cyclades-app / synnefo / neutron / models.py @ d6b24130
History | View | Annotate | Download (10.4 kB)
1 | d6b24130 | Marios Kogias | # Copyright 2011-2012 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | d6b24130 | Marios Kogias | #
|
3 | d6b24130 | Marios Kogias | # Redistribution and use in source and binary forms, with or without
|
4 | d6b24130 | Marios Kogias | # modification, are permitted provided that the following conditions
|
5 | d6b24130 | Marios Kogias | # are met:
|
6 | d6b24130 | Marios Kogias | #
|
7 | d6b24130 | Marios Kogias | # 1. Redistributions of source code must retain the above copyright
|
8 | d6b24130 | Marios Kogias | # notice, this list of conditions and the following disclaimer.
|
9 | d6b24130 | Marios Kogias | #
|
10 | d6b24130 | Marios Kogias | # 2. Redistributions in binary form must reproduce the above copyright
|
11 | d6b24130 | Marios Kogias | # notice, this list of conditions and the following disclaimer in the
|
12 | d6b24130 | Marios Kogias | # documentation and/or other materials provided with the distribution.
|
13 | d6b24130 | Marios Kogias | #
|
14 | d6b24130 | Marios Kogias | # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
15 | d6b24130 | Marios Kogias | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16 | d6b24130 | Marios Kogias | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
17 | d6b24130 | Marios Kogias | # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
18 | d6b24130 | Marios Kogias | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19 | d6b24130 | Marios Kogias | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
20 | d6b24130 | Marios Kogias | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
21 | d6b24130 | Marios Kogias | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
22 | d6b24130 | Marios Kogias | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
23 | d6b24130 | Marios Kogias | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
24 | d6b24130 | Marios Kogias | # SUCH DAMAGE.
|
25 | d6b24130 | Marios Kogias | #
|
26 | d6b24130 | Marios Kogias | # The views and conclusions contained in the software and documentation are
|
27 | d6b24130 | Marios Kogias | # those of the authors and should not be interpreted as representing official
|
28 | d6b24130 | Marios Kogias | # policies, either expressed or implied, of GRNET S.A.
|
29 | d6b24130 | Marios Kogias | |
30 | d6b24130 | Marios Kogias | import datetime |
31 | d6b24130 | Marios Kogias | |
32 | d6b24130 | Marios Kogias | from copy import deepcopy |
33 | d6b24130 | Marios Kogias | from django.conf import settings |
34 | d6b24130 | Marios Kogias | from django.db import models |
35 | d6b24130 | Marios Kogias | from django.db import IntegrityError |
36 | d6b24130 | Marios Kogias | |
37 | d6b24130 | Marios Kogias | from contextlib import contextmanager |
38 | d6b24130 | Marios Kogias | from hashlib import sha1 |
39 | d6b24130 | Marios Kogias | from snf_django.lib.api import faults |
40 | d6b24130 | Marios Kogias | from django.conf import settings as snf_settings |
41 | d6b24130 | Marios Kogias | |
42 | d6b24130 | Marios Kogias | from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager |
43 | d6b24130 | Marios Kogias | from synnefo.db import pools |
44 | d6b24130 | Marios Kogias | |
45 | d6b24130 | Marios Kogias | from synnefo.db.models import VirtualMachine, QuotaHolderSerial |
46 | d6b24130 | Marios Kogias | |
47 | d6b24130 | Marios Kogias | from synnefo.logic.rapi_pool import (get_rapi_client, |
48 | d6b24130 | Marios Kogias | put_rapi_client) |
49 | d6b24130 | Marios Kogias | |
50 | d6b24130 | Marios Kogias | import logging |
51 | d6b24130 | Marios Kogias | log = logging.getLogger(__name__) |
52 | d6b24130 | Marios Kogias | |
53 | d6b24130 | Marios Kogias | |
54 | d6b24130 | Marios Kogias | |
55 | d6b24130 | Marios Kogias | class Network(models.Model): |
56 | d6b24130 | Marios Kogias | OPER_STATES = ( |
57 | d6b24130 | Marios Kogias | ('PENDING', 'Pending'), # Unused because of lazy networks |
58 | d6b24130 | Marios Kogias | ('ACTIVE', 'Active'), |
59 | d6b24130 | Marios Kogias | ('DELETED', 'Deleted'), |
60 | d6b24130 | Marios Kogias | ('ERROR', 'Error') |
61 | d6b24130 | Marios Kogias | ) |
62 | d6b24130 | Marios Kogias | |
63 | d6b24130 | Marios Kogias | ACTIONS = ( |
64 | d6b24130 | Marios Kogias | ('CREATE', 'Create Network'), |
65 | d6b24130 | Marios Kogias | ('DESTROY', 'Destroy Network'), |
66 | d6b24130 | Marios Kogias | ('ADD', 'Add server to Network'), |
67 | d6b24130 | Marios Kogias | ('REMOVE', 'Remove server from Network'), |
68 | d6b24130 | Marios Kogias | ) |
69 | d6b24130 | Marios Kogias | |
70 | d6b24130 | Marios Kogias | RSAPI_STATE_FROM_OPER_STATE = { |
71 | d6b24130 | Marios Kogias | 'PENDING': 'PENDING', |
72 | d6b24130 | Marios Kogias | 'ACTIVE': 'ACTIVE', |
73 | d6b24130 | Marios Kogias | 'DELETED': 'DELETED', |
74 | d6b24130 | Marios Kogias | 'ERROR': 'ERROR' |
75 | d6b24130 | Marios Kogias | } |
76 | d6b24130 | Marios Kogias | |
77 | d6b24130 | Marios Kogias | FLAVORS = { |
78 | d6b24130 | Marios Kogias | 'CUSTOM': {
|
79 | d6b24130 | Marios Kogias | 'mode': 'bridged', |
80 | d6b24130 | Marios Kogias | 'link': settings.DEFAULT_BRIDGE,
|
81 | d6b24130 | Marios Kogias | 'mac_prefix': settings.DEFAULT_MAC_PREFIX,
|
82 | d6b24130 | Marios Kogias | 'tags': None, |
83 | d6b24130 | Marios Kogias | 'desc': "Basic flavor used for a bridged network", |
84 | d6b24130 | Marios Kogias | }, |
85 | d6b24130 | Marios Kogias | 'IP_LESS_ROUTED': {
|
86 | d6b24130 | Marios Kogias | 'mode': 'routed', |
87 | d6b24130 | Marios Kogias | 'link': settings.DEFAULT_ROUTING_TABLE,
|
88 | d6b24130 | Marios Kogias | 'mac_prefix': settings.DEFAULT_MAC_PREFIX,
|
89 | d6b24130 | Marios Kogias | 'tags': 'ip-less-routed', |
90 | d6b24130 | Marios Kogias | 'desc': "Flavor used for an IP-less routed network using" |
91 | d6b24130 | Marios Kogias | " Proxy ARP",
|
92 | d6b24130 | Marios Kogias | }, |
93 | d6b24130 | Marios Kogias | 'MAC_FILTERED': {
|
94 | d6b24130 | Marios Kogias | 'mode': 'bridged', |
95 | d6b24130 | Marios Kogias | 'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
|
96 | d6b24130 | Marios Kogias | 'mac_prefix': 'pool', |
97 | d6b24130 | Marios Kogias | 'tags': 'private-filtered', |
98 | d6b24130 | Marios Kogias | 'desc': "Flavor used for bridged networks that offer isolation" |
99 | d6b24130 | Marios Kogias | " via filtering packets based on their src "
|
100 | d6b24130 | Marios Kogias | " MAC (ebtables)",
|
101 | d6b24130 | Marios Kogias | }, |
102 | d6b24130 | Marios Kogias | 'PHYSICAL_VLAN': {
|
103 | d6b24130 | Marios Kogias | 'mode': 'bridged', |
104 | d6b24130 | Marios Kogias | 'link': 'pool', |
105 | d6b24130 | Marios Kogias | 'mac_prefix': settings.DEFAULT_MAC_PREFIX,
|
106 | d6b24130 | Marios Kogias | 'tags': 'physical-vlan', |
107 | d6b24130 | Marios Kogias | 'desc': "Flavor used for bridged network that offer isolation" |
108 | d6b24130 | Marios Kogias | " via dedicated physical vlan",
|
109 | d6b24130 | Marios Kogias | }, |
110 | d6b24130 | Marios Kogias | } |
111 | d6b24130 | Marios Kogias | |
112 | d6b24130 | Marios Kogias | name = models.CharField('Network Name', max_length=128) |
113 | d6b24130 | Marios Kogias | userid = models.CharField('User ID of the owner', max_length=128, |
114 | d6b24130 | Marios Kogias | null=True, db_index=True) |
115 | d6b24130 | Marios Kogias | flavor = models.CharField('Flavor', max_length=32, null=False) |
116 | d6b24130 | Marios Kogias | mode = models.CharField('Network Mode', max_length=16, null=True) |
117 | d6b24130 | Marios Kogias | link = models.CharField('Network Link', max_length=32, null=True) |
118 | d6b24130 | Marios Kogias | mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False) |
119 | d6b24130 | Marios Kogias | tags = models.CharField('Network Tags', max_length=128, null=True) |
120 | d6b24130 | Marios Kogias | public = models.BooleanField(default=False, db_index=True) |
121 | d6b24130 | Marios Kogias | created = models.DateTimeField(auto_now_add=True)
|
122 | d6b24130 | Marios Kogias | updated = models.DateTimeField(auto_now=True)
|
123 | d6b24130 | Marios Kogias | deleted = models.BooleanField('Deleted', default=False, db_index=True) |
124 | d6b24130 | Marios Kogias | state = models.CharField(choices=OPER_STATES, max_length=32,
|
125 | d6b24130 | Marios Kogias | default='PENDING')
|
126 | d6b24130 | Marios Kogias | machines = models.ManyToManyField(VirtualMachine, |
127 | d6b24130 | Marios Kogias | through='NetworkInterface', related_name='neutron_machines') |
128 | d6b24130 | Marios Kogias | action = models.CharField(choices=ACTIONS, max_length=32, null=True, |
129 | d6b24130 | Marios Kogias | default=None)
|
130 | d6b24130 | Marios Kogias | drained = models.BooleanField("Drained", default=False, null=False) # this is the opposite of admin_state_up |
131 | d6b24130 | Marios Kogias | floating_ip_pool = models.BooleanField('Floating IP Pool', null=False, |
132 | d6b24130 | Marios Kogias | default=False)
|
133 | d6b24130 | Marios Kogias | #serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
|
134 | d6b24130 | Marios Kogias | # null=True)
|
135 | d6b24130 | Marios Kogias | |
136 | d6b24130 | Marios Kogias | objects = ForUpdateManager() |
137 | d6b24130 | Marios Kogias | |
138 | d6b24130 | Marios Kogias | def __unicode__(self): |
139 | d6b24130 | Marios Kogias | return "<Network: %s>" % str(self.id) |
140 | d6b24130 | Marios Kogias | |
141 | d6b24130 | Marios Kogias | @property
|
142 | d6b24130 | Marios Kogias | def backend_id(self): |
143 | d6b24130 | Marios Kogias | """Return the backend id by prepending backend-prefix."""
|
144 | d6b24130 | Marios Kogias | if not self.id: |
145 | d6b24130 | Marios Kogias | raise Network.InvalidBackendIdError("self.id is None") |
146 | d6b24130 | Marios Kogias | return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id)) |
147 | d6b24130 | Marios Kogias | |
148 | d6b24130 | Marios Kogias | @property
|
149 | d6b24130 | Marios Kogias | def backend_tag(self): |
150 | d6b24130 | Marios Kogias | """Return the network tag to be used in backend
|
151 | d6b24130 | Marios Kogias |
|
152 | d6b24130 | Marios Kogias | """
|
153 | d6b24130 | Marios Kogias | if self.tags: |
154 | d6b24130 | Marios Kogias | return self.tags.split(',') |
155 | d6b24130 | Marios Kogias | else:
|
156 | d6b24130 | Marios Kogias | return []
|
157 | d6b24130 | Marios Kogias | |
158 | d6b24130 | Marios Kogias | def create_backend_network(self, backend=None): |
159 | d6b24130 | Marios Kogias | """Create corresponding BackendNetwork entries."""
|
160 | d6b24130 | Marios Kogias | |
161 | d6b24130 | Marios Kogias | backends = [backend] if backend else\ |
162 | d6b24130 | Marios Kogias | Backend.objects.filter(offline=False)
|
163 | d6b24130 | Marios Kogias | for backend in backends: |
164 | d6b24130 | Marios Kogias | backend_exists =\ |
165 | d6b24130 | Marios Kogias | BackendNetwork.objects.filter(backend=backend, network=self)\
|
166 | d6b24130 | Marios Kogias | .exists() |
167 | d6b24130 | Marios Kogias | if not backend_exists: |
168 | d6b24130 | Marios Kogias | BackendNetwork.objects.create(backend=backend, network=self)
|
169 | d6b24130 | Marios Kogias | |
170 | d6b24130 | Marios Kogias | def get_pool(self, with_lock=True): |
171 | d6b24130 | Marios Kogias | if not self.pool_id: |
172 | d6b24130 | Marios Kogias | self.pool = IPPoolTable.objects.create(available_map='', |
173 | d6b24130 | Marios Kogias | reserved_map='',
|
174 | d6b24130 | Marios Kogias | size=0)
|
175 | d6b24130 | Marios Kogias | self.save()
|
176 | d6b24130 | Marios Kogias | objects = IPPoolTable.objects |
177 | d6b24130 | Marios Kogias | if with_lock:
|
178 | d6b24130 | Marios Kogias | objects = objects.select_for_update() |
179 | d6b24130 | Marios Kogias | return objects.get(id=self.pool_id).pool |
180 | d6b24130 | Marios Kogias | |
181 | d6b24130 | Marios Kogias | def reserve_address(self, address): |
182 | d6b24130 | Marios Kogias | pool = self.get_pool()
|
183 | d6b24130 | Marios Kogias | pool.reserve(address) |
184 | d6b24130 | Marios Kogias | pool.save() |
185 | d6b24130 | Marios Kogias | |
186 | d6b24130 | Marios Kogias | def release_address(self, address): |
187 | d6b24130 | Marios Kogias | pool = self.get_pool()
|
188 | d6b24130 | Marios Kogias | pool.put(address) |
189 | d6b24130 | Marios Kogias | pool.save() |
190 | d6b24130 | Marios Kogias | |
191 | d6b24130 | Marios Kogias | class InvalidBackendIdError(Exception): |
192 | d6b24130 | Marios Kogias | def __init__(self, value): |
193 | d6b24130 | Marios Kogias | self.value = value
|
194 | d6b24130 | Marios Kogias | |
195 | d6b24130 | Marios Kogias | def __str__(self): |
196 | d6b24130 | Marios Kogias | return repr(self.value) |
197 | d6b24130 | Marios Kogias | |
198 | d6b24130 | Marios Kogias | class InvalidBackendMsgError(Exception): |
199 | d6b24130 | Marios Kogias | def __init__(self, opcode, status): |
200 | d6b24130 | Marios Kogias | self.opcode = opcode
|
201 | d6b24130 | Marios Kogias | self.status = status
|
202 | d6b24130 | Marios Kogias | |
203 | d6b24130 | Marios Kogias | def __str__(self): |
204 | d6b24130 | Marios Kogias | return repr('<opcode: %s, status: %s>' |
205 | d6b24130 | Marios Kogias | % (self.opcode, self.status)) |
206 | d6b24130 | Marios Kogias | |
207 | d6b24130 | Marios Kogias | class InvalidActionError(Exception): |
208 | d6b24130 | Marios Kogias | def __init__(self, action): |
209 | d6b24130 | Marios Kogias | self._action = action
|
210 | d6b24130 | Marios Kogias | |
211 | d6b24130 | Marios Kogias | def __str__(self): |
212 | d6b24130 | Marios Kogias | return repr(str(self._action)) |
213 | d6b24130 | Marios Kogias | |
214 | d6b24130 | Marios Kogias | class Subnet(models.Model): |
215 | d6b24130 | Marios Kogias | |
216 | d6b24130 | Marios Kogias | name = models.CharField('Network Name', max_length=128) |
217 | d6b24130 | Marios Kogias | subnet_id = models.CharField('ID of the subnet', max_length=128, |
218 | d6b24130 | Marios Kogias | null=True, db_index=True) |
219 | d6b24130 | Marios Kogias | network_id = models.ForeignKey('Network')
|
220 | d6b24130 | Marios Kogias | # subnet will be null for IPv6 only networks
|
221 | d6b24130 | Marios Kogias | subnet = models.CharField('Subnet', max_length=32, null=True) |
222 | d6b24130 | Marios Kogias | # subnet6 will be null for IPv4 only networks
|
223 | d6b24130 | Marios Kogias | subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True) |
224 | d6b24130 | Marios Kogias | gateway = models.CharField('Gateway', max_length=32, null=True) |
225 | d6b24130 | Marios Kogias | gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True) |
226 | d6b24130 | Marios Kogias | dhcp = models.BooleanField('DHCP', default=True) |
227 | d6b24130 | Marios Kogias | #pool = models.OneToOneField('IPPoolTable', related_name='network',
|
228 | d6b24130 | Marios Kogias | # default=lambda: IPPoolTable.objects.create(
|
229 | d6b24130 | Marios Kogias | # available_map='',
|
230 | d6b24130 | Marios Kogias | # reserved_map='',
|
231 | d6b24130 | Marios Kogias | # size=0),
|
232 | d6b24130 | Marios Kogias | # null=True)
|
233 | d6b24130 | Marios Kogias | |
234 | d6b24130 | Marios Kogias | class NetworkInterface(models.Model): |
235 | d6b24130 | Marios Kogias | STATES = ( |
236 | d6b24130 | Marios Kogias | ("ACTIVE", "Active"), |
237 | d6b24130 | Marios Kogias | ("BUILDING", "Building"), |
238 | d6b24130 | Marios Kogias | ) |
239 | d6b24130 | Marios Kogias | |
240 | d6b24130 | Marios Kogias | name = models.CharField('Network Name', max_length=128) |
241 | d6b24130 | Marios Kogias | machine = models.ForeignKey(VirtualMachine, related_name='neutron_nics')
|
242 | d6b24130 | Marios Kogias | network = models.ForeignKey(Network, related_name='neutron_nics')
|
243 | d6b24130 | Marios Kogias | created = models.DateTimeField(auto_now_add=True)
|
244 | d6b24130 | Marios Kogias | updated = models.DateTimeField(auto_now=True)
|
245 | d6b24130 | Marios Kogias | index = models.IntegerField(null=True)
|
246 | d6b24130 | Marios Kogias | mac = models.CharField(max_length=32, null=True, unique=True) |
247 | d6b24130 | Marios Kogias | ipv4 = models.CharField(max_length=15, null=True) |
248 | d6b24130 | Marios Kogias | ipv6 = models.CharField(max_length=100, null=True) |
249 | d6b24130 | Marios Kogias | #firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
|
250 | d6b24130 | Marios Kogias | # max_length=30, null=True)
|
251 | d6b24130 | Marios Kogias | dirty = models.BooleanField(default=False)
|
252 | d6b24130 | Marios Kogias | state = models.CharField(max_length=32, null=False, default="ACTIVE", |
253 | d6b24130 | Marios Kogias | choices=STATES) |
254 | d6b24130 | Marios Kogias | admin_state_up = models.BooleanField(default=False, db_index=True) |
255 | d6b24130 | Marios Kogias | |
256 | d6b24130 | Marios Kogias | def __unicode__(self): |
257 | d6b24130 | Marios Kogias | return "<%s:vm:%s network:%s ipv4:%s ipv6:%s>" % \ |
258 | d6b24130 | Marios Kogias | (self.index, self.machine_id, self.network_id, self.ipv4, |
259 | d6b24130 | Marios Kogias | self.ipv6)
|
260 | d6b24130 | Marios Kogias | |
261 | d6b24130 | Marios Kogias | @property
|
262 | d6b24130 | Marios Kogias | def is_floating_ip(self): |
263 | d6b24130 | Marios Kogias | network = self.network
|
264 | d6b24130 | Marios Kogias | if self.ipv4 and network.floating_ip_pool: |
265 | d6b24130 | Marios Kogias | return network.floating_ips.filter(machine=self.machine, |
266 | d6b24130 | Marios Kogias | ipv4=self.ipv4,
|
267 | d6b24130 | Marios Kogias | deleted=False).exists() |