Statistics
| Branch: | Tag: | Revision:

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()