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