Revision 633f6952

b/snf-cyclades-app/synnefo/neutron/models.py
162 162
        return "<Subnet %s>" % str(self.id)
163 163

  
164 164

  
165
class SecurityGroup(models.Model):
166
    name = models.CharField('group name', max_length=128)
167

  
168

  
165 169
class NetworkInterface(models.Model):
166 170
    STATES = (
167 171
        ("ACTIVE", "Active"),
......
185 189
    state = models.CharField(max_length=32, null=False, default="ACTIVE",
186 190
                             choices=STATES)
187 191
    admin_state_up = models.BooleanField(default=False, db_index=True)
192
    security_groups = models.ManyToManyField(SecurityGroup, null=True)
193
    deleted = models.BooleanField('Deleted', default=False, db_index=True)
194
    objects = ForUpdateManager()
188 195

  
189
    def __unicode__(self):
190
        return "<%s:vm:%s network:%s ipv4:%s ipv6:%s>" % \
191
            (self.index, self.machine_id, self.network_id, self.ipv4,
192
             self.ipv6)
193

  
194
    @property
195
    def is_floating_ip(self):
196
        network = self.network
197
        if self.ipv4 and network.floating_ip_pool:
198
            return network.floating_ips.filter(machine=self.machine,
199
                                               ipv4=self.ipv4,
200
                                               deleted=False).exists()
b/snf-cyclades-app/synnefo/neutron/models_factory.py
50 50

  
51 51
    machine = factory.SubFactory(VirtualMachineFactory)
52 52
    network = factory.SubFactory(NetworkFactory)
53

  
53
    name = random_string(5)
54 54

  
55 55
class SubnetFactory(factory.DjangoModelFactory):
56 56
    FACTORY_FOR = models.Subnet
......
60 60
    cidr = factory.Sequence(lambda n: '192.168.{0}.0/24'.format(n))
61 61
    dhcp = True
62 62
    gateway = factory.Sequence(lambda n: '192.168.{0}.1'.format(n))
63

  
64

  
65
class SecurityGroupFactory(factory.DjangoModelFactory):
66
    FACTORY_FOR = models.SecurityGroup
67

  
68
    name = factory.LazyAttribute(lambda self: random_string(30))
b/snf-cyclades-app/synnefo/neutron/port_views.py
10 10
from synnefo.logic import backend
11 11
from django.template.loader import render_to_string
12 12
from synnefo.api import util
13
from models import NetworkInterface
13
from models import NetworkInterface, SecurityGroup
14 14

  
15 15
from logging import getLogger
16 16

  
......
19 19

  
20 20
def demux(request):
21 21
    if request.method == 'GET':
22
        return HttpResponse("list ports")
23
        #return list_ports(request)
22
        #return HttpResponse("list ports")
23
        return list_ports(request)
24 24
    elif request.method == 'POST':
25 25
        #return create_port(request)
26 26
        return HttpResponse("create port")
......
31 31
def port_demux(request, offset):
32 32

  
33 33
    if request.method == 'GET':
34
        return HttpResponse("get single port")
35
        #return get_port(request,offset)
34
        #return HttpResponse("get single port")
35
        return get_port(request,offset)
36 36
    elif request.method == 'DELETE':
37
        return HttpResponse("delete port")
38
        #return delete_port(request,offset)
37
        #return HttpResponse("delete port")
38
        return delete_port(request,offset)
39 39
    elif request.method == 'PUT':
40
        return HttpResponse("put port")
41
        #return update_port(request,offset)
40
        #return HttpResponse("put port")
41
        return update_port(request,offset)
42 42
    else:
43 43
        return api.api_method_not_allowed(request)
44

  
45

  
46
@api.api_method(http_method='GET', user_required=True, logger=log)
47
def list_ports(request, detail=False):
48

  
49
    log.debug('list_ports detail=%s', detail)
50

  
51
    user_ports = NetworkInterface.objects.filter(network__userid=request.user_uniq)
52

  
53
    user_ports = utils.filter_modified_since(request, objects=user_ports)
54

  
55
    ports = [port_to_dict(network, detail)
56
        for port in user_ports.order_by('id')]
57

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

  
64
    return HttpResponse(data, status=200)
65

  
66

  
67
@api.api_method(http_method='GET', user_required=True, logger=log)
68
def get_port(request, port_id):
69
    log.debug('get_port_details %s', port_id)
70
    port = get_port_fromdb(port_id, request.user_uniq)
71

  
72
    #needs discussion
73
    if port.deleted:
74
        raise api.faults.BadRequest("Port has been deleted.")
75
    else:
76
        portdict = port_to_dict(port)
77
    return render_port(request, portdict)
78

  
79

  
80

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

  
87

  
88
    if port.deleted:
89
        raise api.faults.BadRequest("Network has been deleted.")
90

  
91

  
92
    '''
93
    skip the backend part...
94
    release the ips associated with the port
95
    '''
96

  
97
    #the following has to leave when fix the backend thing
98
    port.deleted = True
99
    port.save()
100

  
101
    return HttpResponse(status=204)
102

  
103

  
104
@api.api_method(http_method='PUT', user_required=True, logger=log)
105
def update_port(request, port_id):
106
    '''
107
    You can update only name, admin_state_up, security_groups
108
    '''
109
    port = get_port_fromdb(port_id, request.user_uniq, for_update=True)
110
    info = utils.get_request_dict(request)
111
    try:
112
        info = info["port"]
113
    except KeyError:
114
        raise api.faults.BadRequest("Malformed request")
115

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

  
133
        #clear the old security groups
134
        port.security_groups.clear()
135

  
136
        #add the new groups
137
        for group in sg_list:
138
            port.security_groups.add(group)
139
    except KeyError:
140
        pass
141

  
142
    port.save()
143
    portdict = port_to_dict(port)
144
    return render_port(request, portdict, 200)
145

  
146

  
147

  
148
#util functions
149

  
150

  
151
def port_to_dict(port, detail=True):
152
    d = {'id': str(port.id), 'name': port.name}
153
    if detail:
154
        d['user_id'] = port.network.userid
155
        d['tenant_id'] = port.network.userid
156
        d['device_id'] = port.machine.id
157
        d['admin_state_up'] = "true"
158
        d['mac_address'] = port.mac
159
        d['status'] = port.state
160
        if port.subnet:
161
            d['fixed_ips'] = [{"ip_address" :port.ipv4, "subnet" :port.subnet.id}]
162
        else:
163
            d['fixed_ips'] = []
164
        d['security_groups'] = [str(sg.id) for sg in port.security_groups.all()]
165
    return d
166

  
167

  
168

  
169
def render_port(request, portdict, status=200):
170
    if request.serialization == 'xml':
171
        data = render_to_string('network.xml', {'port': portdict})
172
    else:
173
        data = json.dumps({'port': portdict})
174
    return HttpResponse(data, status=status)
175

  
176
def get_port_fromdb(port_id, user_id, for_update=False):
177
    """
178
    Return a NetworkInteface instance or raise ItemNotFound.
179
    This is the same as util.get_network
180
    """
181
    try:
182
        port_id = int(port_id)
183
        objects = NetworkInterface.objects
184
        if for_update:
185
            objects = objects.select_for_update()
186

  
187
        return objects.get(network__userid=user_id, id=port_id)
188
    except (ValueError, NetworkInterface.DoesNotExist):
189
        raise api.faults.ItemNotFound('Port not found.')
b/snf-cyclades-app/synnefo/neutron/tests/api.py
13 13
NETWORKS_URL = join_urls(NEUTRON_URL, "networks/")
14 14
SUBNETS_URL = join_urls(NEUTRON_URL, "subnets/")
15 15
ROUTERS_URL = join_urls(NEUTRON_URL, "routers/")
16

  
16
PORTS_URL = join_urls(NEUTRON_URL, "ports/")
17 17

  
18 18
class NetworkTest(BaseAPITest):
19 19

  
......
115 115
        self.assertEqual(response.status_code, 400)
116 116

  
117 117

  
118
class PortTest(BaseAPITest):
119
    def test_get_ports(self):
120
        url = join_urls(PORTS_URL)
121
        response = self.get(url)
122
        self.assertEqual(response.status_code, 200)
123
        ports = json.loads(response.content)
124
        self.assertEqual(ports, {"ports": []})
125

  
126
    def test_get_port(self):
127
        nic = mf.NetworkInterfaceFactory.create()
128
        url = join_urls(PORTS_URL, str(nic.id))
129
        response = self.get(url, user=nic.network.userid)
130
        self.assertEqual(response.status_code, 200)
131

  
132
    def test_delete_port(self):
133
        nic = mf.NetworkInterfaceFactory.create()
134
        url = join_urls(PORTS_URL, str(nic.id))
135
        response = self.delete(url, user=nic.network.userid)
136
        self.assertEqual(response.status_code, 204)
137

  
138
    def test_update_port_name(self):
139
        nic = mf.NetworkInterfaceFactory.create()
140
        url = join_urls(PORTS_URL, str(nic.id))
141
        request = {'port': {"name":"test-name"}}
142
        response = self.put(url, params=json.dumps(request), user=nic.network.userid)
143
        self.assertEqual(response.status_code, 200)
144
        res = json.loads(response.content)
145
        self.assertEqual(res['port']['name'], "test-name")
146

  
147
    def test_update_port_sg_unfound(self):
148
        sg1 = mf.SecurityGroupFactory.create()
149
        nic = mf.NetworkInterfaceFactory.create()
150
        nic.security_groups.add(sg1)
151
        nic.save()
152
        url = join_urls(PORTS_URL, str(nic.id))
153
        request = {'port': {"security_groups":["123"]}}
154
        response = self.put(url, params=json.dumps(request), user=nic.network.userid)
155
        self.assertEqual(response.status_code, 404)
156

  
157
    def test_update_port_sg(self):
158
        sg1 = mf.SecurityGroupFactory.create()
159
        sg2 = mf.SecurityGroupFactory.create()
160
        sg3 = mf.SecurityGroupFactory.create()
161
        nic = mf.NetworkInterfaceFactory.create()
162
        nic.security_groups.add(sg1)
163
        nic.save()
164
        url = join_urls(PORTS_URL, str(nic.id))
165
        request = {'port': {"security_groups":[str(sg2.id), str(sg3.id)]}}
166
        response = self.put(url, params=json.dumps(request), user=nic.network.userid)
167
        res = json.loads(response.content)
168
        self.assertEqual(res['port']['security_groups'], [str(sg2.id), str(sg3.id)])
169

  
118 170
class RouterTest(BaseAPITest):
119 171
    def test_list_empty_routers(self):
120 172
        response = self.get(ROUTERS_URL)

Also available in: Unified diff