Revision 5db2001a

b/snf-cyclades-app/synnefo/api/ports.py
1
# Copyright 2011-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

  
1 34
from django.conf import settings
2 35
from django.conf.urls import patterns
3

  
4 36
from django.http import HttpResponse
5 37
from django.utils import simplejson as json
6 38
from django.db import transaction
7 39
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 40
from django.template.loader import render_to_string
41

  
42
from snf_django.lib import api
43

  
15 44
from synnefo.api import util
16 45
from synnefo.db.models import NetworkInterface, SecurityGroup, IPAddress
17 46

  
......
24 53
    (r'^(?:/|.json|.xml)?$', 'demux'),
25 54
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'port_demux'))
26 55

  
56

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

  
37 65

  
38
def port_demux(request, offset):
66
def port_demux(request, port_id):
39 67

  
40 68
    if request.method == 'GET':
41
        #return HttpResponse("get single port")
42
        return get_port(request, offset)
69
        return get_port_details(request, port_id)
43 70
    elif request.method == 'DELETE':
44
        #return HttpResponse("delete port")
45
        return delete_port(request, offset)
71
        return delete_port(request, port_id)
46 72
    elif request.method == 'PUT':
47
        #return HttpResponse("put port")
48
        return update_port(request, offset)
73
        return update_port(request, port_id)
49 74
    else:
50 75
        return api.api_method_not_allowed(request)
51 76

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

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

  
64 89
    if request.serialization == 'xml':
65
        data = render_to_string('list_networks.xml', {
66
            "ports": ports})
90
        data = render_to_string('list_ports.xml', {
91
            "ports": port_dicts})
67 92
    else:
68
        data = json.dumps({'ports': ports})
93
        data = json.dumps({'ports': port_dicts})
69 94

  
70 95
    return HttpResponse(data, status=200)
71 96

  
72 97

  
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 98
@api.api_method(http_method='POST', user_required=True, logger=log)
143 99
@transaction.commit_manually
144 100
def create_port(request):
145 101
    '''
146 102
    '''
147 103
    user_id = request.user_uniq
148
    req = utils.get_request_dict(request)
104
    req = api.utils.get_request_dict(request)
149 105
    log.info('create_port %s', req)
150 106
    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")
107
        port_dict = api.utils.get_attribute(req, "port")
108
        net_id = api.utils.get_attribute(port_dict, "network_id")
109
        dev_id = api.utils.get_attribute(port_dict, "device_id")
157 110

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

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

  
162
        try:
163
            name = info['name']
164
        except KeyError:
165
            name = "random_name"
115
        name = api.utils.get_attribute(port_dict, "name", required=False)
116

  
117
        if name is None:
118
            name = ""
166 119

  
167 120
        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:
121
        security_groups = api.utils.get_attribute(port_dict,
122
                                                  "security_groups",
123
                                                  required=False)
124
        #validate security groups
125
        # like get security group from db
126
        if security_groups:
127
            for gid in security_groups:
173 128
                try:
174 129
                    sg = SecurityGroup.objects.get(id=int(gid))
175 130
                    sg_list.append(sg)
176 131
                except (ValueError, SecurityGroup.DoesNotExist):
177 132
                    raise api.faults.ItemNotFound("Not valid security group")
178
        except KeyError:
179
            pass
180 133

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

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

  
149
                                     # FIXME
150
                                     address="192.168.0." + str(subn.id))
199 151

  
200 152
    except:
201 153
        transaction.rollback()
......
206 158
        transaction.commit()
207 159
        log.info("commit")
208 160

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

  
212 163
    return response
213 164

  
214 165

  
166
@api.api_method(http_method='GET', user_required=True, logger=log)
167
def get_port_details(request, port_id):
168
    log.debug('get_port_details %s', port_id)
169
    port = util.get_port(port_id, request.user_uniq)
170
    return render_port(request, port_to_dict(port))
171

  
172

  
173
@api.api_method(http_method='PUT', user_required=True, logger=log)
174
def update_port(request, port_id):
175
    '''
176
    You can update only name, security_groups
177
    '''
178
    port = util.get_port(port_id, request.user_uniq, for_update=True)
179
    req = api.utils.get_request_dict(request)
180

  
181
    port_info = api.utils.get_attribute(req, "port", required=True)
182
    name = api.utils.get_attribute(port_info, "name", required=False)
183

  
184
    if name:
185
        port.name = name
186

  
187
    security_groups = api.utils.get_attribute(port_info, "security_groups",
188
                                              required=False)
189
    if security_groups:
190
        sg_list = []
191
        #validate security groups
192
        # like get security group from db
193
        for gid in security_groups:
194
            try:
195
                sg = SecurityGroup.objects.get(id=int(gid))
196
                sg_list.append(sg)
197
            except (ValueError, SecurityGroup.DoesNotExist):
198
                raise api.faults.ItemNotFound("Not valid security group")
199

  
200
        #clear the old security groups
201
        port.security_groups.clear()
202

  
203
        #add the new groups
204
        port.security_groups.add(*sg_list)
205

  
206
    port.save()
207
    return render_port(request, port_to_dict(port), 200)
208

  
209

  
210
@api.api_method(http_method='DELETE', user_required=True, logger=log)
211
@transaction.commit_on_success
212
def delete_port(request, port_id):
213
    log.info('delete_port %s', port_id)
214
    port = util.get_port(port_id, request.user_uniq, for_update=True)
215
    '''
216
    FIXME delete the port
217
    skip the backend part...
218
    release the ips associated with the port
219
    '''
220
    return HttpResponse(status=204)
221

  
215 222
#util functions
216 223

  
217 224

  
......
226 233
        d['status'] = port.state
227 234
        d['device_owner'] = port.device_owner
228 235
        d['network_id'] = str(port.network.id)
236
        d['updated'] = api.utils.isoformat(port.updated)
237
        d['created'] = api.utils.isoformat(port.created)
229 238
        d['fixed_ips'] = []
230 239
        for ip in port.ips.all():
231 240
            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()]
241
                                      "subnet": str(ip.subnet.id)})
242
        sg_list = list(port.security_groups.values_list('id', flat=True))
243
        d['security_groups'] = map(str, sg_list)
244

  
235 245
    return d
236 246

  
237 247

  
238 248
def render_port(request, portdict, status=200):
239 249
    if request.serialization == 'xml':
240
        data = render_to_string('network.xml', {'port': portdict})
250
        data = render_to_string('port.xml', {'port': portdict})
241 251
    else:
242 252
        data = json.dumps({'port': portdict})
243 253
    return HttpResponse(data, status=status)
b/snf-cyclades-app/synnefo/api/templates/list_networks.xml
4 4
  {% for network in networks %}
5 5
  <network id="{{ network.id }}" name="{{ network.name }}"{% if detail %} updated="{{ network.updated }}" created="{{ network.created }}"{% endif %}>
6 6

  
7
  {% if network.servers %}
8
  <servers>
9
    {% for server_id in network.servers.values %}
10
    <server id="{{ server_id }}"></server>
7
  {% if network.subnets %}
8
  <subnets>
9
    {% for subnet_id in network.subnets.values %}
10
    <subnet id="{{ server_id }}"></subnet>
11 11
    {% endfor %}
12
  </servers>
12
  </subnets>
13 13
  {% endif %}
14 14

  
15 15
  </network>
b/snf-cyclades-app/synnefo/api/templates/list_ports.xml
1
{% spaceless %}
2
<?xml version="1.0" encoding="UTF-8"?>
3
<ports xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom">
4
  {% for port in ports %}
5
  <port id="{{ port.id }}" name="{{ port.name }}"{% if detail %} updated="{{ port.updated }}" created="{{ port.created }}" mac_address="{{port.mac_address}}"{% endif %}>
6

  
7
  {% if port.fixed_ips %}
8
  <fixed_ips>
9
    {% for ip in port.fixed_ips.values %}
10
    <ip>
11
    <ip_address id="{{ ip.ip_address }}"></ip_address>
12
    <subnet id="{{ ip.subnet }}"></subnet>
13
    </ip>
14
    {% endfor %}
15
  </fixed_ips>
16
  {% endif %}
17

  
18
  </port>
19
  {% endfor %}
20
</ports>
21
{% endspaceless %}
b/snf-cyclades-app/synnefo/api/templates/network.xml
2 2
<?xml version="1.0" encoding="UTF-8"?>
3 3
<network xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" id="{{ network.id }}" name="{{ network.name }}" updated="{{ network.updated }}" created="{{ network.created }}">
4 4

  
5
<servers>
6
  {% for server_id in network.servers.values %}
7
  <server id="{{ server_id }}"></server>
8
  {% endfor %}
9
</servers>
10

  
5
 <subnets>
6
    {% for subnet_id in network.subnets.values %}
7
    <subnet id="{{ server_id }}"></subnet>
8
    {% endfor %}
9
    </subnets> 
11 10
</network>
11

  
12 12
{% endspaceless %}
b/snf-cyclades-app/synnefo/api/templates/port.xml
1
{% spaceless %}
2
<?xml version="1.0" encoding="UTF-8"?>
3
<port xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" id="{{ port.id }}" name="{{ port.name }}" updated="{{ port.updated }}" created="{{ port.created }}" mac_address="{{port.mac_address}}">
4

  
5
    <fixed_ips>
6
        {% for ip in port.fixed_ips.values %}
7
        <ip>
8
            <ip_address id="{{ ip.ip_address }}"></ip_address>
9
            <subnet id="{{ ip.subnet }}"></subnet>
10
        </ip>
11
        {% endfor %}
12
   </fixed_ips>
13
{% endspaceless %}
b/snf-cyclades-app/synnefo/api/tests/networks.py
167 167
        response = self.delete(url)
168 168
        self.assertItemNotFound(response)
169 169

  
170
    def test_put_network(self):
171
        test_net = dbmf.NetworkFactory()
172
        url = join_urls(NETWORKS_URL, str(test_net.id))
173
        request = {
174
            "network": {
175
                "name": "new_name"}
176
        }
177
        response = self.put(url, params=json.dumps(request),
178
                            user=test_net.userid)
179
        self.assertEqual(response.status_code, 200)
180

  
181 170
    def test_put_network_wrong_data(self):
182 171
        test_net = dbmf.NetworkFactory()
183 172
        url = join_urls(NETWORKS_URL, str(test_net.id))
b/snf-cyclades-app/synnefo/api/tests/ports.py
1
# Copyright 2011-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.i
33

  
34
from snf_django.utils.testing import (BaseAPITest, override_settings)
1 35
from snf_django.utils.testing import BaseAPITest
2 36
from django.utils import simplejson as json
3 37
from synnefo.cyclades_settings import cyclades_services
4 38
from synnefo.lib.services import get_service_path
5 39
from synnefo.lib import join_urls
6
import json
7 40
import synnefo.db.models_factory as dbmf
8 41

  
9 42
COMPUTE_URL = get_service_path(cyclades_services, 'compute',
......
48 81

  
49 82
    def test_update_port_sg_unfound(self):
50 83
        sg1 = dbmf.SecurityGroupFactory.create()
51
        nic =dbmf.NetworkInterfaceFactory.create(device_owner='vm')
84
        nic = dbmf.NetworkInterfaceFactory.create(device_owner='vm')
52 85
        nic.security_groups.add(sg1)
53 86
        nic.save()
54 87
        url = join_urls(PORTS_URL, str(nic.id))
......
72 105
        self.assertEqual(res['port']['security_groups'],
73 106
                         [str(sg2.id), str(sg3.id)])
74 107

  
75

  
76 108
    def test_create_port_no_network(self):
77 109
        request = {
78 110
            "port": {

Also available in: Unified diff