Revision d6b24130

b/snf-cyclades-app/synnefo/neutron/models.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
#
7
#   1. Redistributions of source code must retain the above copyright
8
#      notice, this list of conditions and the following disclaimer.
9
#
10
#  2. Redistributions in binary form must reproduce the above copyright
11
#     notice, this list of conditions and the following disclaimer in the
12
#     documentation and/or other materials provided with the distribution.
13
#
14
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
# SUCH DAMAGE.
25
#
26
# The views and conclusions contained in the software and documentation are
27
# those of the authors and should not be interpreted as representing official
28
# policies, either expressed or implied, of GRNET S.A.
29

  
30
import datetime
31

  
32
from copy import deepcopy
33
from django.conf import settings
34
from django.db import models
35
from django.db import IntegrityError
36

  
37
from contextlib import contextmanager
38
from hashlib import sha1
39
from snf_django.lib.api import faults
40
from django.conf import settings as snf_settings
41

  
42
from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager
43
from synnefo.db import pools
44

  
45
from synnefo.db.models import VirtualMachine, QuotaHolderSerial
46

  
47
from synnefo.logic.rapi_pool import (get_rapi_client,
48
                                     put_rapi_client)
49

  
50
import logging
51
log = logging.getLogger(__name__)
52

  
53

  
54

  
55
class Network(models.Model):
56
    OPER_STATES = (
57
        ('PENDING', 'Pending'),  # Unused because of lazy networks
58
        ('ACTIVE', 'Active'),
59
        ('DELETED', 'Deleted'),
60
        ('ERROR', 'Error')
61
    )
62

  
63
    ACTIONS = (
64
        ('CREATE', 'Create Network'),
65
        ('DESTROY', 'Destroy Network'),
66
        ('ADD', 'Add server to Network'),
67
        ('REMOVE', 'Remove server from Network'),
68
    )
69

  
70
    RSAPI_STATE_FROM_OPER_STATE = {
71
        'PENDING': 'PENDING',
72
        'ACTIVE': 'ACTIVE',
73
        'DELETED': 'DELETED',
74
        'ERROR': 'ERROR'
75
    }
76

  
77
    FLAVORS = {
78
        'CUSTOM': {
79
            'mode': 'bridged',
80
            'link': settings.DEFAULT_BRIDGE,
81
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
82
            'tags': None,
83
            'desc': "Basic flavor used for a bridged network",
84
        },
85
        'IP_LESS_ROUTED': {
86
            'mode': 'routed',
87
            'link': settings.DEFAULT_ROUTING_TABLE,
88
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
89
            'tags': 'ip-less-routed',
90
            'desc': "Flavor used for an IP-less routed network using"
91
                    " Proxy ARP",
92
        },
93
        'MAC_FILTERED': {
94
            'mode': 'bridged',
95
            'link': settings.DEFAULT_MAC_FILTERED_BRIDGE,
96
            'mac_prefix': 'pool',
97
            'tags': 'private-filtered',
98
            'desc': "Flavor used for bridged networks that offer isolation"
99
                    " via filtering packets based on their src "
100
                    " MAC (ebtables)",
101
        },
102
        'PHYSICAL_VLAN': {
103
            'mode': 'bridged',
104
            'link': 'pool',
105
            'mac_prefix': settings.DEFAULT_MAC_PREFIX,
106
            'tags': 'physical-vlan',
107
            'desc': "Flavor used for bridged network that offer isolation"
108
                    " via dedicated physical vlan",
109
        },
110
    }
111

  
112
    name = models.CharField('Network Name', max_length=128)
113
    userid = models.CharField('User ID of the owner', max_length=128,
114
                              null=True, db_index=True)
115
    flavor = models.CharField('Flavor', max_length=32, null=False)
116
    mode = models.CharField('Network Mode', max_length=16, null=True)
117
    link = models.CharField('Network Link', max_length=32, null=True)
118
    mac_prefix = models.CharField('MAC Prefix', max_length=32, null=False)
119
    tags = models.CharField('Network Tags', max_length=128, null=True)
120
    public = models.BooleanField(default=False, db_index=True)
121
    created = models.DateTimeField(auto_now_add=True)
122
    updated = models.DateTimeField(auto_now=True)
123
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
124
    state = models.CharField(choices=OPER_STATES, max_length=32,
125
                             default='PENDING')
126
    machines = models.ManyToManyField(VirtualMachine,
127
                                      through='NetworkInterface', related_name='neutron_machines')
128
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
129
                              default=None)
130
    drained = models.BooleanField("Drained", default=False, null=False) # this is the opposite of admin_state_up
131
    floating_ip_pool = models.BooleanField('Floating IP Pool', null=False,
132
                                           default=False)
133
    #serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
134
    #                           null=True)
135

  
136
    objects = ForUpdateManager()
137

  
138
    def __unicode__(self):
139
        return "<Network: %s>" % str(self.id)
140

  
141
    @property
142
    def backend_id(self):
143
        """Return the backend id by prepending backend-prefix."""
144
        if not self.id:
145
            raise Network.InvalidBackendIdError("self.id is None")
146
        return "%snet-%s" % (settings.BACKEND_PREFIX_ID, str(self.id))
147

  
148
    @property
149
    def backend_tag(self):
150
        """Return the network tag to be used in backend
151

  
152
        """
153
        if self.tags:
154
            return self.tags.split(',')
155
        else:
156
            return []
157

  
158
    def create_backend_network(self, backend=None):
159
        """Create corresponding BackendNetwork entries."""
160

  
161
        backends = [backend] if backend else\
162
            Backend.objects.filter(offline=False)
163
        for backend in backends:
164
            backend_exists =\
165
                BackendNetwork.objects.filter(backend=backend, network=self)\
166
                                      .exists()
167
            if not backend_exists:
168
                BackendNetwork.objects.create(backend=backend, network=self)
169

  
170
    def get_pool(self, with_lock=True):
171
        if not self.pool_id:
172
            self.pool = IPPoolTable.objects.create(available_map='',
173
                                                   reserved_map='',
174
                                                   size=0)
175
            self.save()
176
        objects = IPPoolTable.objects
177
        if with_lock:
178
            objects = objects.select_for_update()
179
        return objects.get(id=self.pool_id).pool
180

  
181
    def reserve_address(self, address):
182
        pool = self.get_pool()
183
        pool.reserve(address)
184
        pool.save()
185

  
186
    def release_address(self, address):
187
        pool = self.get_pool()
188
        pool.put(address)
189
        pool.save()
190

  
191
    class InvalidBackendIdError(Exception):
192
        def __init__(self, value):
193
            self.value = value
194

  
195
        def __str__(self):
196
            return repr(self.value)
197

  
198
    class InvalidBackendMsgError(Exception):
199
        def __init__(self, opcode, status):
200
            self.opcode = opcode
201
            self.status = status
202

  
203
        def __str__(self):
204
            return repr('<opcode: %s, status: %s>'
205
                        % (self.opcode, self.status))
206

  
207
    class InvalidActionError(Exception):
208
        def __init__(self, action):
209
            self._action = action
210

  
211
        def __str__(self):
212
            return repr(str(self._action))
213

  
214
class Subnet(models.Model):
215

  
216
    name = models.CharField('Network Name', max_length=128)
217
    subnet_id = models.CharField('ID of the subnet', max_length=128,
218
                              null=True, db_index=True)
219
    network_id = models.ForeignKey('Network')
220
    # subnet will be null for IPv6 only networks
221
    subnet = models.CharField('Subnet', max_length=32, null=True)
222
    # subnet6 will be null for IPv4 only networks
223
    subnet6 = models.CharField('IPv6 Subnet', max_length=64, null=True)
224
    gateway = models.CharField('Gateway', max_length=32, null=True)
225
    gateway6 = models.CharField('IPv6 Gateway', max_length=64, null=True)
226
    dhcp = models.BooleanField('DHCP', default=True)
227
    #pool = models.OneToOneField('IPPoolTable', related_name='network',
228
    #                            default=lambda: IPPoolTable.objects.create(
229
    #                                                        available_map='',
230
    #                                                        reserved_map='',
231
    #                                                        size=0),
232
    #                           null=True)
233

  
234
class NetworkInterface(models.Model):
235
    STATES = (
236
        ("ACTIVE", "Active"),
237
        ("BUILDING", "Building"),
238
    )
239

  
240
    name = models.CharField('Network Name', max_length=128)
241
    machine = models.ForeignKey(VirtualMachine, related_name='neutron_nics')
242
    network = models.ForeignKey(Network, related_name='neutron_nics')
243
    created = models.DateTimeField(auto_now_add=True)
244
    updated = models.DateTimeField(auto_now=True)
245
    index = models.IntegerField(null=True)
246
    mac = models.CharField(max_length=32, null=True, unique=True)
247
    ipv4 = models.CharField(max_length=15, null=True)
248
    ipv6 = models.CharField(max_length=100, null=True)
249
    #firewall_profile = models.CharField(choices=FIREWALL_PROFILES,
250
    #                                    max_length=30, null=True)
251
    dirty = models.BooleanField(default=False)
252
    state = models.CharField(max_length=32, null=False, default="ACTIVE",
253
                             choices=STATES)
254
    admin_state_up = models.BooleanField(default=False, db_index=True)
255

  
256
    def __unicode__(self):
257
        return "<%s:vm:%s network:%s ipv4:%s ipv6:%s>" % \
258
            (self.index, self.machine_id, self.network_id, self.ipv4,
259
             self.ipv6)
260

  
261
    @property
262
    def is_floating_ip(self):
263
        network = self.network
264
        if self.ipv4 and network.floating_ip_pool:
265
            return network.floating_ips.filter(machine=self.machine,
266
                                               ipv4=self.ipv4,
267
                                               deleted=False).exists()
b/snf-cyclades-app/synnefo/neutron/network_views.py
1
from django.http import HttpResponse
2
from django.utils import simplejson as json
3
from django.db import transaction
4
from django.db.models import Q
5
from synnefo.db.pools import EmptyPool
6
from synnefo.db.utils import validate_mac
7
from django.conf import settings
8
from snf_django.lib import api
9
from snf_django.lib.api import utils
10
from synnefo.logic import backend
11
from django.template.loader import render_to_string
12
from synnefo.api import util
13
from models import Network
14

  
15
from logging import getLogger
16

  
17
from synnefo import quotas
18

  
19

  
20
log = getLogger(__name__)
21

  
22
def demux(request):
23
    if request.method == 'GET':
24
        #return HttpResponse("in network get")
25
        return list_networks(request)
26
    elif request.method == 'POST':
27
        #return create_network(request)
28
        return HttpResponse("in network post")
29
    else:
30
        return api.api_method_not_allowed(request)
31

  
32
def network_demux(request,offset):
33

  
34
    if request.method == 'GET':
35
        return get_network(request,offset)
36
        #return HttpResponse("in network det get")
37
    elif request.method == 'DELETE':
38
        return delete_network(request,offset)
39
        #return HttpResponse("in network det delete")
40
    elif request.method == 'PUT':
41
        #return update_network(request,offset)
42
        return HttpResponse("in network det put")
43
    else:
44
        return api.api_method_not_allowed(request)
45

  
46
@api.api_method(http_method='GET', user_required=True, logger=log)
47
def list_networks(request):
48
    log.debug('list_networks')
49

  
50
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
51
            Q(public=True))
52

  
53
    user_networks = user_networks.filter(deleted=False)
54

  
55
    networks = [network_to_dict(network)
56
                for network in user_networks.order_by('id')]
57

  
58
    if request.serialization == 'xml':
59
        data = render_to_string('list_networks.xml', {
60
            'networks': networks })
61
    else:
62
        data = json.dumps({'networks': networks})
63

  
64
    return HttpResponse(data, status=200)
65

  
66
@api.api_method(http_method='POST', user_required=True, logger=log)
67
@transaction.commit_manually
68
def create_network (request):
69
    '''
70
    This operation does not require a rest body. If specified, the body might
71
    include one or more of the following attributes:
72

  
73
    name: a string specifying a symbolic name for the network, which is not
74
        required to be unique;
75

  
76
    admin_state_up: a bool value specifying the administrative status of the network;
77

  
78
    2 more attirbutes for administrative users (not supported)
79
    '''
80

  
81
    try:
82
        user_id = request.user_uniq
83
        if request.raw_post_data:
84
            req = utils.get_request_dict(request)
85
            log.info('create_network %s', req)
86
            try:
87
                 d = req['network']
88
                 name = d['name']
89
            except KeyError:
90
                 raise api.faults.BadRequest("Malformed request")
91

  
92
            flavor = d.get("type", None)
93
            if flavor is None:
94
                #raise faults.BadRequest("Missing request parameter 'type'")
95
                # set default flavor
96
                #FIX ME!!!
97
                flavor="MAC_FILTERED"
98
                log.info("not found flavor")
99
            elif flavor not in Network.FLAVORS.keys():
100
                raise api.faults.BadRequest("Invalid network type '%s'" % flavor)
101
                log.info("found flavor 1")
102
            elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
103
                log.info("found flavor2")
104
                raise api.faults.Forbidden("Can not create network of type '%s'" %flavor)
105
            # Get and validate flavor. Flavors are still exposed as 'type' in the
106
            # API.
107
        else:
108
            name = ""
109
            flavor = "MAC_FILTERED" # this is the default FIX ME
110

  
111
        try:
112
            mode, link, mac_prefix, tags = util.values_from_flavor(flavor)
113
            validate_mac(mac_prefix + "0:00:00:00")
114
            network = Network.objects.create(
115
                name=name,
116
                userid=user_id,
117
                flavor=flavor,
118
                mode=mode,
119
                link=link,
120
                mac_prefix=mac_prefix,
121
                tags=tags,
122
                action='CREATE',
123
                state='ACTIVE')
124
        except EmptyPool:
125
            log.error("Failed to allocate resources for network of type: %s",
126
            flavor)
127
            raise api.faults.ServiceUnavailable("Failed to allocate network"
128
                " resources")
129

  
130
        # Issue commission to Quotaholder and accept it since at the end of
131
        # this transaction the Network object will be created in the DB.
132
        # Note: the following call does a commit!
133
        #quotas.issue_and_accept_commission(network)
134
        # COME BACK....
135

  
136
    except:
137
        transaction.rollback()
138
        log.info("roll")
139
        raise
140
    else:
141
        transaction.commit()
142
        log.info("commit")
143
    networkdict = network_to_dict(network)
144
    response = render_network(request, networkdict, status=201)
145

  
146
    return response
147

  
148
@api.api_method(http_method='GET', user_required=True, logger=log)
149
def get_network(request,network_id):
150
    log.debug('get_network_details %s', network_id)
151
    net = get_network_fromdb(network_id, request.user_uniq)
152

  
153
    #needs discussion
154
    if net.deleted:
155
        raise api.faults.BadRequest("Network has been deleted.")
156
    else:
157
        netdict = network_to_dict(net)
158
        return render_network(request, netdict)
159

  
160
@api.api_method(http_method='DELETE', user_required=True, logger=log)
161
@transaction.commit_on_success
162
def delete_network(request,network_id):
163
    log.info('delete_network %s', network_id)
164
    net = get_network_fromdb(network_id, request.user_uniq, for_update=True)
165

  
166
    log.info(net.name)
167
    if net.public:
168
        raise api.faults.Forbidden('Can not delete the public network.')
169

  
170
    if net.deleted:
171
        raise api.faults.BadRequest("Network has been deleted.")
172

  
173
    if net.machines.all():  # Nics attached on network
174
        raise api.faults.NetworkInUse('Machines are connected to network.')
175

  
176
    net.action = 'DESTROY'
177
    net.save()
178
    '''
179
    skip the backend part...
180
    backend_networks = net.backend_networks.exclude(operstate="DELETED")
181
    for bnet in backend_networks:
182
        backend.delete_network(net, bnet.backend)
183
    if not backend_networks:
184
        backend.update_network_state(net)
185
    '''
186

  
187
    #the following has to leave when fix the backend thing
188
    net.deleted = True
189
    net.save()
190

  
191
    return HttpResponse(status=204)
192

  
193

  
194
@api.api_method(http_method='PUT', user_required=True, logger=log)
195
def update_network(request,network_id):
196
    '''
197
    You can update only name and admin_state_up
198
    '''
199
    net = get_network_fromdb(network_id, request.user_uniq, for_update=True)
200
    info = utils.get_request_dict(request)
201

  
202
    updatable = set(["name","admin_state_up"])
203
    try:
204
		new_vals = info["network"]
205
    except Keyerror:
206
        raise api.faults.BadRequest("Malformed request")
207

  
208
    for key,val in new_vals.iteritems():
209
        if key in updatable:
210
            setattr(net,key,val)
211
        else:
212
            raise api.faults.BadRequest("Wrong field update")
213
	net.save()
214
    #net_dic = network_to_dict(net)
215
    #data = json.dumps({"network":net_dic})
216
    netdict = network_to_dict(net)
217
    return render_network(request, netdict, 200)
218

  
219
def network_to_dict(network):
220
    d = {'id': str(network.id), 'name': network.name}
221
    d['links'] = util.network_to_links(network.id)
222
    d['user_id'] = network.userid
223
    d['tenant_id'] = network.userid
224
    d['type'] = network.flavor
225
    d['updated'] = utils.isoformat(network.updated)
226
    d['created'] = utils.isoformat(network.created)
227
    d['status'] = network.state
228
    d['public'] = network.public
229
    d['admin_state_up'] = "true"
230
    subnet_cidr = [s.subnet_id for s in network.subnet_set.all()]
231
    d['subnets'] = subnet_cidr
232
    return d
233

  
234

  
235

  
236

  
237

  
238
def render_network(request, networkdict, status=200):
239
    if request.serialization == 'xml':
240
        data = render_to_string('network.xml', {'network': networkdict})
241
    else:
242
        data = json.dumps({'network': networkdict})
243
    return HttpResponse(data, status=status)
244

  
245

  
246

  
247

  
248
def get_network_fromdb(network_id, user_id, for_update=False):
249
    """
250
    Return a Network instance or raise ItemNotFound.
251
    This is the same as util.get_network
252
    """
253
    try:
254
        network_id = int(network_id)
255
        objects = Network.objects
256
        if for_update:
257
            objects = objects.select_for_update()
258
        return objects.get(Q(userid=user_id) | Q(public=True), id=network_id)
259
    except (ValueError, Network.DoesNotExist):
260
        raise api.faults.ItemNotFound('Network not found.')
b/snf-cyclades-app/synnefo/neutron/port_views.py
1
from django.http import HttpResponse
2
from django.utils import simplejson as json
3
from django.db import transaction
4
from django.db.models import Q
5
from synnefo.db.pools import EmptyPool
6
from synnefo.db.utils import validate_mac
7
from django.conf import settings
8
from snf_django.lib import api
9
from snf_django.lib.api import utils
10
from synnefo.logic import backend
11
from django.template.loader import render_to_string
12
from synnefo.api import util
13
from models import NetworkInterface
14

  
15
from logging import getLogger
16

  
17

  
18

  
19
log = getLogger(__name__)
20

  
21
def demux(request):
22
    if request.method == 'GET':
23
        return HttpResponse("list ports")
24
        #return list_ports(request)
25
    elif request.method == 'POST':
26
        #return create_port(request)
27
        return HttpResponse("create port")
28
    else:
29
        return api.api_method_not_allowed(request)
30

  
31
def port_demux(request,offset):
32

  
33
    if request.method == 'GET':
34
        return HttpResponse("get single port")
35
        #return get_port(request,offset)
36
    elif request.method == 'DELETE':
37
        return HttpResponse("delete port")
38
        #return delete_port(request,offset)
39
    elif request.method == 'PUT':
40
        return HttpResponse("put port")
41
        #return update_port(request,offset)
42
    else:
43
        return api.api_method_not_allowed(request)
44
"""
45
@api.api_method(http_method='GET', user_required=True, logger=log)
46
def list_ports(request):
47
    log.debug('list_networks')
48

  
49
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
50
            Q(public=True))
51

  
52
    user_networks = user_networks.filter(deleted=False)
53

  
54
    networks = [network_to_dict(network)
55
                for network in user_networks.order_by('id')]
56

  
57
    if request.serialization == 'xml':
58
        data = render_to_string('list_networks.xml', {
59
            'networks': networks })
60
    else:
61
        data = json.dumps({'networks': networks})
62

  
63
    return HttpResponse(data, status=200)
64
"""
65
@api.api_method(http_method='POST', user_required=True, logger=log)
66
@transaction.commit_manually
67
def create_port (request):
68
    '''
69
    '''
70

  
71
    try:
72
        user_id = request.user_uniq
73
        req = utils.get_request_dict(request)
74
        log.info('create_network %s', req)
75
        try:
76
             d = req['port']
77
             name = d['name']
78
        except KeyError:
79
             raise api.faults.BadRequest("Malformed request")
80

  
81
        flavor = d.get("type", None)
82
        if flavor is None:
83
            #raise faults.BadRequest("Missing request parameter 'type'")
84
            # set default flavor
85
            #FIX ME!!!
86
            flavor="MAC_FILTERED"
87
            log.info("not found flavor")
88
        elif flavor not in Network.FLAVORS.keys():
89
            raise api.faults.BadRequest("Invalid network type '%s'" % flavor)
90
            log.info("found flavor 1")
91
        elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
92
            log.info("found flavor2")
93
            raise api.faults.Forbidden("Can not create network of type '%s'" %flavor)
94
        # Get and validate flavor. Flavors are still exposed as 'type' in the
95
        # API.
96

  
97
        try:
98
            mode, link, mac_prefix, tags = util.values_from_flavor(flavor)
99
            validate_mac(mac_prefix + "0:00:00:00")
100
            network = Network.objects.create(
101
                name=name,
102
                userid=user_id,
103
                flavor=flavor,
104
                mode=mode,
105
                link=link,
106
                mac_prefix=mac_prefix,
107
                tags=tags,
108
                action='CREATE',
109
                state='ACTIVE')
110
        except EmptyPool:
111
            log.error("Failed to allocate resources for network of type: %s",
112
            flavor)
113
            raise api.faults.ServiceUnavailable("Failed to allocate network"
114
                " resources")
115

  
116
        # Issue commission to Quotaholder and accept it since at the end of
117
        # this transaction the Network object will be created in the DB.
118
        # Note: the following call does a commit!
119
        #quotas.issue_and_accept_commission(network)
120
        # COME BACK....
121

  
122
    except:
123
        transaction.rollback()
124
        log.info("roll")
125
        raise
126
    else:
127
        transaction.commit()
128
        log.info("commit")
129
    networkdict = network_to_dict(network)
130
    response = render_network(request, networkdict, status=201)
131

  
132
    return response
133

  
b/snf-cyclades-app/synnefo/neutron/subnet_views.py
1
from django.http import HttpResponse
2
from django.utils import simplejson as json
3

  
4
from snf_django.lib import api
5
from snf_django.lib.api import utils
6

  
7
from models import Network
8

  
9
from logging import getLogger
10

  
11
log = getLogger(__name__)
12

  
13
def demux(request):
14
    if request.method == 'GET':
15
        return list_networks(request)
16
        #return HttpResponse("in subnet get")
17
    elif request.method == 'POST':
18
        #return create_network(request)
19
        return HttpResponse("in subnet POST")
20
    else:
21
        return api.api_method_not_allowed(request)
22

  
23
def subnet_demux(request,offset):
24
    if request.method == 'GET':
25
        #return get_network(request,offset)
26
        return HttpResponse("in subnet det get")
27
    elif request.method == 'DELETE':
28
        #return delete_network(request,offset)
29
        return HttpResponse("in subnet det delete")
30
    elif request.method == 'PUT':
31
        #return update_network(request,offset)
32
        return HttpResponse("in subnet det put")
33
    else:
34
        return api.api_method_not_allowed(request)
35

  
36
@api.api_method(http_method='GET', user_required=True, logger=log)
37
def list_networks(request):
38
    user_networks = Network.objects.all()
39
    networks_dict = [network_to_dict(net)
40
                    for net in user_networks.order_by('name')]
41
    data = json.dumps({'networks': networks_dict})
42
    return HttpResponse(data, status=200)
43

  
44
@api.api_method(http_method='POST', user_required=True, logger=log)
45
def create_network(request):
46
    dic = utils.get_request_dict(request)
47
    try:
48
    	net = dic["network"]
49
    	name = net["name"]
50
    	user_id = request.user_uniq
51
    except KeyError:
52
        raise api.faults.BadRequest("Wrong data")
53
    # get more info from the JSON
54
    new_net = Network(name=name,user_id=user_id)
55
    new_net.save()
56
    net_dic = network_to_dict(new_net)
57
    data = json.dumps({'network':net_dic})
58
    return HttpResponse("hello world")
59

  
60

  
61
@api.api_method(http_method='GET', user_required=True, logger=log)
62
def get_network(request,offset):
63
    try:
64
        net = Network.objects.get(id=offset)
65
    except Network.DoesNotExist:
66
        raise api.faults.ItemNotFound("Network not found")
67

  
68
    net_dic = network_to_dict(net)
69
    data = json.dumps({'network': net_dic})
70
    return HttpResponse(data,status=200)
71

  
72
@api.api_method(http_method='DELETE', user_required=True, logger=log)
73
def delete_network(request,offset):
74
   try:
75
        net = Network.objects.get(id=offset)
76
   except Network.DoesNotExist:
77
        raise api.faults.ItemNotFound("Network not found")
78

  
79
   net.delete()
80
   return HttpResponse(status=204)
81

  
82
@api.api_method(http_method='PUT', user_required=True, logger=log)
83
def update_network(request,offset):
84
    try:
85
        net = Network.objects.get(id=offset)
86
    except Network.DoesNotExist:
87
        raise api.faults.ItemNotFound("Network not found")
88
    info = utils.get_request_dict(request)
89
    updatable = set(["name","driver"])
90
    try:
91
		new_vals = info["network"]
92
    except Keyerror:
93
        raise api.faults.BadRequest()
94

  
95
    for key,val in new_vals.iteritems():
96
        if key in updatable:
97
            setattr(net,key,val)
98
        else:
99
            raise api.faults.BadRequest()
100
	net.save()
101
    net_dic = network_to_dict(net)
102
    data = json.dumps({"network":net_dic})
103
    return HttpResponse(data)
104

  
105
def network_to_dict(net):
106
    d = dict(name=net.name,driver=net.driver,driver_ops=net.driver_ops,id=net.id,user_id=net.user_id)
107
    return d
108

  
109

  
b/snf-cyclades-app/synnefo/neutron/tests/__init__.py
1
from synnefo.neutron.tests.api import *
b/snf-cyclades-app/synnefo/neutron/tests/api.py
1
from snf_django.utils.testing import BaseAPITest
2
from django.utils import simplejson as json
3
from synnefo.cyclades_settings import cyclades_services
4
from synnefo.lib.services import get_service_path
5
from synnefo.lib import join_urls
6

  
7

  
8
NEUTRON_URL = get_service_path(cyclades_services, "neutron", "v2.0")
9
NETWORKS_URL = join_urls(NEUTRON_URL, "networks/")
10

  
11

  
12
class NetworkTest(BaseAPITest):
13
    def test_list_networks(self):
14
        response = self.get(NETWORKS_URL)
15
        self.assertSuccess(response)
16
        networks = json.loads(response.content)
17
        self.assertEqual(networks, {'networks': []})
18

  
19
    def test_create_network(self):
20
        request = {}
21
        response = self.post(NETWORKS_URL, json.dumps(request), "json")
22
        code = response.status_code
23
        self.assertEqual(code, 501)
24

  
25
    def test_get_unfound_network(self):
26
        url = join_urls(NETWORKS_URL,"123")
27
        response = self.get(url)
28
        self.assertItemNotFound(response)
29

  
30
    def test_delete_unfound_network(self):
31
        url = join_urls(NETWORKS_URL,"123")
32
        response = self.delete(url)
33
        self.assertItemNotFound(response)
34

  
35

  
36
    def marios(self):
37
        print "hello world"
b/snf-cyclades-app/synnefo/neutron/urls.py
1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from django.conf.urls.defaults import patterns, include
35
from django.http import HttpResponseNotAllowed
36
from snf_django.lib.api import api_endpoint_not_found
37

  
38
from synnefo.neutron import views
39

  
40

  
41

  
42

  
43
neutron_v2_patterns = patterns('',
44
        (r'^networks(?:/|.json|.xml)?$', 'synnefo.neutron.network_views.demux'),
45
        (r'^networks/(\w+)(?:/|.json|.xml)?$', 'synnefo.neutron.network_views.network_demux'),
46
        (r'^subnets(?:/|.json|.xml)?$', 'synnefo.neutron.subnet_views.demux'),
47
        (r'^subnets/(\w+)(?:/|.json|.xml)?$', 'synnefo.neutron.subnet_views.subnet_demux'),
48
        (r'^ports(?:/|.json|.xml)?$', 'synnefo.neutron.port_views.demux'),
49
        (r'^ports/(\w+)(?:/|.json|.xml)?$', 'synnefo.neutron.port_views.port_demux')
50
        )
51

  
52
urlpatterns = patterns(
53
    '',
54
    (r'^v2.0/', include(neutron_v2_patterns)),
55
    (r'^.*', api_endpoint_not_found),
56
)
b/snf-cyclades-app/synnefo/neutron/views.py
1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
import json
35

  
36
from logging import getLogger
37
#from django.conf import settings
38
from django.http import HttpResponse
39

  
40
from snf_django.lib import api
41
from snf_django.lib.api import faults
42

  
43
log = getLogger('synnefo.neutron')
44

  
45

  
46
@api.api_method(http_method="GET", user_required=True, logger=log)
47
def list_networks(request, detail=False):
48
    networks = []
49
    data = json.dumps(networks)
50
    return HttpResponse(data)
51

  
52

  
53
@api.api_method(http_method="POST", user_required=True, logger=log)
54
def add_network(request):
55
    raise faults.NotImplemented(message="Adding a network is not supported.")

Also available in: Unified diff