Revision 593851e0

b/snf-cyclades-app/synnefo/api/ports.py
1
from django.conf import settings
2
from django.conf.urls import patterns
3

  
4
from django.http import HttpResponse
5
from django.utils import simplejson as json
6
from django.db import transaction
7
from django.db.models import Q
8
from synnefo.db.pools import EmptyPool
9
from synnefo.db.utils import validate_mac
10
from django.conf import settings
11
from snf_django.lib import api
12
from snf_django.lib.api import utils
13
from synnefo.logic import backend
14
from django.template.loader import render_to_string
15
from synnefo.api import util
16
from synnefo.db.models import NetworkInterface, SecurityGroup, IPAddress
17

  
18
from logging import getLogger
19

  
20
log = getLogger(__name__)
21

  
22
urlpatterns = patterns(
23
    'synnefo.api.ports',
24
    (r'^(?:/|.json|.xml)?$', 'demux'),
25
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'port_demux'))
26

  
27
def demux(request):
28
    if request.method == 'GET':
29
        #return HttpResponse("list ports")
30
        return list_ports(request)
31
    elif request.method == 'POST':
32
        return create_port(request)
33
        #return HttpResponse("create port")
34
    else:
35
        return api.api_method_not_allowed(request)
36

  
37

  
38
def port_demux(request, offset):
39

  
40
    if request.method == 'GET':
41
        #return HttpResponse("get single port")
42
        return get_port(request, offset)
43
    elif request.method == 'DELETE':
44
        #return HttpResponse("delete port")
45
        return delete_port(request, offset)
46
    elif request.method == 'PUT':
47
        #return HttpResponse("put port")
48
        return update_port(request, offset)
49
    else:
50
        return api.api_method_not_allowed(request)
51

  
52

  
53
@api.api_method(http_method='GET', user_required=True, logger=log)
54
def list_ports(request, detail=False):
55

  
56
    log.debug('list_ports detail=%s', detail)
57

  
58
    user_ports = NetworkInterface.objects.filter(
59
        network__userid=request.user_uniq)
60

  
61
    ports = [port_to_dict(port, detail)
62
             for port in user_ports.order_by('id')]
63

  
64
    if request.serialization == 'xml':
65
        data = render_to_string('list_networks.xml', {
66
            "ports": ports})
67
    else:
68
        data = json.dumps({'ports': ports})
69

  
70
    return HttpResponse(data, status=200)
71

  
72

  
73
@api.api_method(http_method='GET', user_required=True, logger=log)
74
def get_port(request, port_id):
75
    log.debug('get_port_details %s', port_id)
76
    port = util.get_port(port_id, request.user_uniq)
77

  
78
    portdict = port_to_dict(port)
79
    return render_port(request, portdict)
80

  
81

  
82
@api.api_method(http_method='DELETE', user_required=True, logger=log)
83
@transaction.commit_on_success
84
def delete_port(request, port_id):
85
    log.info('delete_port %s', port_id)
86
    port = util.get_port(port_id, request.user_uniq, for_update=True)
87

  
88

  
89
    '''
90
    FIXME delete the port
91
    skip the backend part...
92
    release the ips associated with the port
93
    '''
94

  
95

  
96
    return HttpResponse(status=204)
97

  
98

  
99
@api.api_method(http_method='PUT', user_required=True, logger=log)
100
def update_port(request, port_id):
101
    '''
102
    You can update only name, security_groups
103
    '''
104
    port = util.get_port(port_id, request.user_uniq, for_update=True)
105
    info = utils.get_request_dict(request)
106
    try:
107
        info = info["port"]
108
    except KeyError:
109
        raise api.faults.BadRequest("Malformed request")
110

  
111
    try:
112
        name = info['name']
113
        port.name = name
114
    except KeyError:
115
        pass
116
    sg_list = []
117
    try:
118
        s_groups = info['security_groups']
119
        #validate security groups
120
        # like get security group from db
121
        for gid in s_groups:
122
            try:
123
                sg = SecurityGroup.objects.get(id=int(gid))
124
                sg_list.append(sg)
125
            except (ValueError, SecurityGroup.DoesNotExist):
126
                raise api.faults.ItemNotFound("Not valid security group")
127

  
128
        #clear the old security groups
129
        port.security_groups.clear()
130

  
131
        #add the new groups
132
        for group in sg_list:
133
            port.security_groups.add(group)
134
    except KeyError:
135
        pass
136

  
137
    port.save()
138
    portdict = port_to_dict(port)
139
    return render_port(request, portdict, 200)
140

  
141

  
142
@api.api_method(http_method='POST', user_required=True, logger=log)
143
@transaction.commit_manually
144
def create_port(request):
145
    '''
146
    '''
147
    user_id = request.user_uniq
148
    req = utils.get_request_dict(request)
149
    log.info('create_port %s', req)
150
    try:
151
        try:
152
            info = req['port']
153
            net_id = info['network_id']
154
            dev_id = info['device_id']
155
        except KeyError:
156
            raise api.faults.BadRequest("Malformed request")
157

  
158
        net = util.get_network(net_id, request.user_uniq)
159

  
160
        vm = util.get_vm(dev_id, request.user_uniq)
161

  
162
        try:
163
            name = info['name']
164
        except KeyError:
165
            name = "random_name"
166

  
167
        sg_list = []
168
        try:
169
            s_groups = info['security_groups']
170
            #validate security groups
171
            # like get security group from db
172
            for gid in s_groups:
173
                try:
174
                    sg = SecurityGroup.objects.get(id=int(gid))
175
                    sg_list.append(sg)
176
                except (ValueError, SecurityGroup.DoesNotExist):
177
                    raise api.faults.ItemNotFound("Not valid security group")
178
        except KeyError:
179
            pass
180

  
181
        #create the port
182
        new_port = NetworkInterface.objects.create(name=name,
183
                                                   network=net,
184
                                                   machine=vm,
185
                                                   device_owner="vm",
186
                                                   state="BUILDING")
187
        #add the security groups
188
        new_port.security_groups.add(*sg_list)
189

  
190
        #add every to every subnet of the network
191
        for subn in net.subnets.all():
192
            IPAddress.objects.create(subnet=subn,
193
                                     network=net,
194
                                     nic=new_port,
195
                                     userid=user_id,
196
                                     address="192.168.0."+str(subn.id)  # FIXME
197
                                     )
198

  
199

  
200
    except:
201
        transaction.rollback()
202
        log.info("roll")
203
        raise
204

  
205
    else:
206
        transaction.commit()
207
        log.info("commit")
208

  
209
    portdict = port_to_dict(new_port)
210
    response = render_port(request, portdict, status=201)
211

  
212
    return response
213

  
214

  
215
#util functions
216

  
217

  
218
def port_to_dict(port, detail=True):
219
    d = {'id': str(port.id), 'name': port.name}
220
    if detail:
221
        d['user_id'] = port.network.userid
222
        d['tenant_id'] = port.network.userid
223
        d['device_id'] = str(port.machine.id)
224
        d['admin_state_up'] = True
225
        d['mac_address'] = port.mac
226
        d['status'] = port.state
227
        d['device_owner'] = port.device_owner
228
        d['network_id'] = str(port.network.id)
229
        d['fixed_ips'] = []
230
        for ip in port.ips.all():
231
            d['fixed_ips'].append({"ip_address": ip.address,
232
                                      "subnet": ip.subnet.id})
233
        d['security_groups'] = [str(sg.id)
234
                                for sg in port.security_groups.all()]
235
    return d
236

  
237

  
238
def render_port(request, portdict, status=200):
239
    if request.serialization == 'xml':
240
        data = render_to_string('network.xml', {'port': portdict})
241
    else:
242
        data = json.dumps({'port': portdict})
243
    return HttpResponse(data, status=status)
b/snf-cyclades-app/synnefo/api/tests/__init__.py
34 34
# Import TestCases
35 35
from .servers import *
36 36
from .networks import *
37
from .ports import *
37 38
from .flavors import *
38 39
from .images import *
39 40
from .versions import *
b/snf-cyclades-app/synnefo/api/tests/networks.py
40 40
        test_net = dbmf.NetworkFactory.create()
41 41
        url = join_urls(NETWORKS_URL, str(test_net.id))
42 42
        response = self.get(url, user=test_net.userid)
43
        print response.content
44 43
        # validate response
45 44
        res = json.loads(response.content)
46 45
        net = res['network']
b/snf-cyclades-app/synnefo/api/tests/ports.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
import json
7
import synnefo.db.models_factory as dbmf
8

  
9
COMPUTE_URL = get_service_path(cyclades_services, 'compute',
10
                               version='v2.0')
11
PORTS_URL = join_urls(COMPUTE_URL, "ports/")
12

  
13

  
14
class PortTest(BaseAPITest):
15
    def test_get_ports(self):
16
        url = join_urls(PORTS_URL)
17
        response = self.get(url)
18
        self.assertEqual(response.status_code, 200)
19
        ports = json.loads(response.content)
20
        self.assertEqual(ports, {"ports": []})
21

  
22
    def test_get_port_unfound(self):
23
        url = join_urls(PORTS_URL, "123")
24
        response = self.get(url)
25
        self.assertEqual(response.status_code, 404)
26

  
27
    def test_get_port(self):
28
        nic = dbmf.NetworkInterfaceFactory.create()
29
        url = join_urls(PORTS_URL, str(nic.id))
30
        response = self.get(url, user=nic.network.userid)
31
        self.assertEqual(response.status_code, 200)
32

  
33
    def test_delete_port(self):
34
        nic = dbmf.NetworkInterfaceFactory.create(device_owner='vm')
35
        url = join_urls(PORTS_URL, str(nic.id))
36
        response = self.delete(url, user=nic.network.userid)
37
        self.assertEqual(response.status_code, 204)
38

  
39
    def test_update_port_name(self):
40
        nic = dbmf.NetworkInterfaceFactory.create(device_owner='vm')
41
        url = join_urls(PORTS_URL, str(nic.id))
42
        request = {'port': {"name": "test-name"}}
43
        response = self.put(url, params=json.dumps(request),
44
                            user=nic.network.userid)
45
        self.assertEqual(response.status_code, 200)
46
        res = json.loads(response.content)
47
        self.assertEqual(res['port']['name'], "test-name")
48

  
49
    def test_update_port_sg_unfound(self):
50
        sg1 = dbmf.SecurityGroupFactory.create()
51
        nic =dbmf.NetworkInterfaceFactory.create(device_owner='vm')
52
        nic.security_groups.add(sg1)
53
        nic.save()
54
        url = join_urls(PORTS_URL, str(nic.id))
55
        request = {'port': {"security_groups": ["123"]}}
56
        response = self.put(url, params=json.dumps(request),
57
                            user=nic.network.userid)
58
        self.assertEqual(response.status_code, 404)
59

  
60
    def test_update_port_sg(self):
61
        sg1 = dbmf.SecurityGroupFactory.create()
62
        sg2 = dbmf.SecurityGroupFactory.create()
63
        sg3 = dbmf.SecurityGroupFactory.create()
64
        nic = dbmf.NetworkInterfaceFactory.create(device_owner='vm')
65
        nic.security_groups.add(sg1)
66
        nic.save()
67
        url = join_urls(PORTS_URL, str(nic.id))
68
        request = {'port': {"security_groups": [str(sg2.id), str(sg3.id)]}}
69
        response = self.put(url, params=json.dumps(request),
70
                            user=nic.network.userid)
71
        res = json.loads(response.content)
72
        self.assertEqual(res['port']['security_groups'],
73
                         [str(sg2.id), str(sg3.id)])
74

  
75

  
76
    def test_create_port_no_network(self):
77
        request = {
78
            "port": {
79
                "device_id": "123",
80
                "name": "port1",
81
                "network_id": "123"
82
            }
83
        }
84
        response = self.post(PORTS_URL, params=json.dumps(request))
85
        self.assertEqual(response.status_code, 404)
86

  
87
    def test_create_port(self):
88
        net = dbmf.NetworkFactory.create()
89
        subnet1 = dbmf.IPv4SubnetFactory.create(network=net)
90
        subnet2 = dbmf.IPv6SubnetFactory.create(network=net)
91
        sg1 = dbmf.SecurityGroupFactory.create()
92
        sg2 = dbmf.SecurityGroupFactory.create()
93
        vm = dbmf.VirtualMachineFactory.create(userid=net.userid)
94
        request = {
95
            "port": {
96
                "name": "port1",
97
                "network_id": str(net.id),
98
                "device_id": str(vm.id),
99
                "security_groups": [str(sg1.id), str(sg2.id)]
100
            }
101
        }
102
        response = self.post(PORTS_URL, params=json.dumps(request),
103
                             user=net.userid)
104
        self.assertEqual(response.status_code, 201)
b/snf-cyclades-app/synnefo/api/urls.py
35 35

  
36 36
from snf_django.lib.api import api_endpoint_not_found
37 37
from synnefo.api import (servers, flavors, images, networks, extensions,
38
                         floating_ips)
38
                         ports, floating_ips)
39 39
from synnefo.api.versions import versions_list, version_details
40 40

  
41 41

  
......
48 48
    (r'^flavors', include(flavors)),
49 49
    (r'^images', include(images)),
50 50
    (r'^networks', include(networks)),
51
    (r'^ports', include(ports)),
51 52
    (r'^extensions', include(extensions)),
52 53
    (r'^os-floating-ips', include(floating_ips.ips_urlpatterns)),
53 54
    (r'^os-floating-ip-pools', include(floating_ips.pools_urlpatterns)),
b/snf-cyclades-app/synnefo/api/util.py
226 226
def get_network(network_id, user_id, public=False, for_update=False):
227 227
    """
228 228
    Return a Network instance or raise ItemNotFound.
229
    This is the same as util.get_network
230 229
    """
231 230
    try:
232 231
        objects = Network.objects
......
238 237
    except (ValueError, Network.DoesNotExist):
239 238
        raise faults.ItemNotFound('Network not found.')
240 239

  
240
def get_port(port_id, user_id, for_update=False):
241
     """
242
     Return a NetworkInteface instance or raise ItemNotFound.
243
     """
244
     try:
245
        objects = NetworkInterface.objects
246
        if for_update:
247
            objects = objects.select_for_update()
248

  
249
        port = objects.get(network__userid=user_id, id=port_id)
250

  
251
        if (port.device_owner != "vm") and for_update:
252
            raise api.faults.BadRequest('Can not update non vm port')
253

  
254
        return port
255
     except (ValueError, NetworkInterface.DoesNotExist):
256
         raise faults.ItemNotFound('Port not found.')
241 257

  
242 258
def get_floating_ip(user_id, ipv4, for_update=False):
243 259
    try:
b/snf-cyclades-app/synnefo/db/models_factory.py
239 239
    floating_ip = True
240 240

  
241 241

  
242
class SecurityGroupFactory(factory.DjangoModelFactory):
243
    FACTORY_FOR = models.SecurityGroup
244

  
245
    name = factory.LazyAttribute(lambda self: random_string(30))
246

  
247

  
242 248
class BridgePoolTableFactory(factory.DjangoModelFactory):
243 249
    FACTORY_FOR = models.BridgePoolTable
244 250

  

Also available in: Unified diff