Claudine Chiu
Committed by Gerrit Code Review

REST API's for tenants, virtual networks, virtual devices ad virtual ports.

Change-Id: I80abe14a083fce3dc6246118af8874028109388f

REST API's for tenants, virtual networks, virtual devices ad virtual ports.

Change-Id: Ib6c3d69d396e57822bae23f5bf3101c8b9c0b95c
...@@ -26,6 +26,10 @@ import org.onosproject.cluster.ControllerNode; ...@@ -26,6 +26,10 @@ import org.onosproject.cluster.ControllerNode;
26 import org.onosproject.codec.CodecService; 26 import org.onosproject.codec.CodecService;
27 import org.onosproject.codec.JsonCodec; 27 import org.onosproject.codec.JsonCodec;
28 import org.onosproject.core.Application; 28 import org.onosproject.core.Application;
29 +import org.onosproject.incubator.net.virtual.TenantId;
30 +import org.onosproject.incubator.net.virtual.VirtualDevice;
31 +import org.onosproject.incubator.net.virtual.VirtualNetwork;
32 +import org.onosproject.incubator.net.virtual.VirtualPort;
29 import org.onosproject.net.Annotations; 33 import org.onosproject.net.Annotations;
30 import org.onosproject.net.ConnectPoint; 34 import org.onosproject.net.ConnectPoint;
31 import org.onosproject.net.Device; 35 import org.onosproject.net.Device;
...@@ -126,6 +130,10 @@ public class CodecManager implements CodecService { ...@@ -126,6 +130,10 @@ public class CodecManager implements CodecService {
126 registerCodec(McastRoute.class, new McastRouteCodec()); 130 registerCodec(McastRoute.class, new McastRouteCodec());
127 registerCodec(DeviceKey.class, new DeviceKeyCodec()); 131 registerCodec(DeviceKey.class, new DeviceKeyCodec());
128 registerCodec(Region.class, new RegionCodec()); 132 registerCodec(Region.class, new RegionCodec());
133 + registerCodec(TenantId.class, new TenantIdCodec());
134 + registerCodec(VirtualNetwork.class, new VirtualNetworkCodec());
135 + registerCodec(VirtualDevice.class, new VirtualDeviceCodec());
136 + registerCodec(VirtualPort.class, new VirtualPortCodec());
129 log.info("Started"); 137 log.info("Started");
130 } 138 }
131 139
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.codec.CodecContext;
20 +import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.incubator.net.virtual.TenantId;
22 +
23 +
24 +import static com.google.common.base.Preconditions.checkNotNull;
25 +import static org.onlab.util.Tools.nullIsIllegal;
26 +
27 +/**
28 + * Codec for the TenantId class.
29 + */
30 +public class TenantIdCodec extends JsonCodec<TenantId> {
31 +
32 + // JSON field names
33 + private static final String TENANT_ID = "id";
34 +
35 + private static final String NULL_TENANT_MSG = "TenantId cannot be null";
36 + private static final String MISSING_MEMBER_MSG = " member is required in TenantId";
37 +
38 + @Override
39 + public ObjectNode encode(TenantId tenantId, CodecContext context) {
40 + checkNotNull(tenantId, NULL_TENANT_MSG);
41 +
42 + ObjectNode result = context.mapper().createObjectNode()
43 + .put(TENANT_ID, tenantId.id().toString());
44 +
45 + return result;
46 + }
47 +
48 + @Override
49 + public TenantId decode(ObjectNode json, CodecContext context) {
50 + if (json == null || !json.isObject()) {
51 + return null;
52 + }
53 +
54 + TenantId tenantId = TenantId.tenantId(extractMember(TENANT_ID, json));
55 +
56 + return tenantId;
57 + }
58 +
59 + private String extractMember(String key, ObjectNode json) {
60 + return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
61 + }
62 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.codec.CodecContext;
20 +import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
22 +import org.onosproject.incubator.net.virtual.NetworkId;
23 +import org.onosproject.incubator.net.virtual.VirtualDevice;
24 +import org.onosproject.net.DeviceId;
25 +
26 +import static com.google.common.base.Preconditions.checkNotNull;
27 +import static org.onlab.util.Tools.nullIsIllegal;
28 +
29 +/**
30 + * Codec for the VirtualDevice class.
31 + */
32 +public class VirtualDeviceCodec extends JsonCodec<VirtualDevice> {
33 +
34 + // JSON field names
35 + private static final String ID = "deviceId";
36 + private static final String NETWORK_ID = "networkId";
37 +
38 + private static final String NULL_OBJECT_MSG = "VirtualDevice cannot be null";
39 + private static final String MISSING_MEMBER_MSG = " member is required in VirtualDevice";
40 +
41 + @Override
42 + public ObjectNode encode(VirtualDevice vDev, CodecContext context) {
43 + checkNotNull(vDev, NULL_OBJECT_MSG);
44 +
45 + ObjectNode result = context.mapper().createObjectNode()
46 + .put(ID, vDev.id().toString())
47 + .put(NETWORK_ID, vDev.networkId().toString());
48 +
49 + return result;
50 + }
51 +
52 + @Override
53 + public VirtualDevice decode(ObjectNode json, CodecContext context) {
54 + if (json == null || !json.isObject()) {
55 + return null;
56 + }
57 +
58 + DeviceId dId = DeviceId.deviceId(extractMember(ID, json));
59 + NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
60 + return new DefaultVirtualDevice(nId, dId);
61 + }
62 +
63 + private String extractMember(String key, ObjectNode json) {
64 + return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
65 + }
66 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.codec.CodecContext;
20 +import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
22 +import org.onosproject.incubator.net.virtual.NetworkId;
23 +import org.onosproject.incubator.net.virtual.TenantId;
24 +import org.onosproject.incubator.net.virtual.VirtualNetwork;
25 +
26 +import static com.google.common.base.Preconditions.checkNotNull;
27 +import static org.onlab.util.Tools.nullIsIllegal;
28 +
29 +/**
30 + * Codec for the VirtualNetwork class.
31 + */
32 +public class VirtualNetworkCodec extends JsonCodec<VirtualNetwork> {
33 +
34 + // JSON field names
35 + private static final String NETWORK_ID = "networkId";
36 + private static final String TENANT_ID = "tenantId";
37 +
38 + private static final String NULL_OBJECT_MSG = "VirtualNetwork cannot be null";
39 + private static final String MISSING_MEMBER_MSG = " member is required in VirtualNetwork";
40 +
41 + @Override
42 + public ObjectNode encode(VirtualNetwork vnet, CodecContext context) {
43 + checkNotNull(vnet, NULL_OBJECT_MSG);
44 +
45 + ObjectNode result = context.mapper().createObjectNode()
46 + .put(NETWORK_ID, vnet.id().toString())
47 + .put(TENANT_ID, vnet.tenantId().toString());
48 +
49 + return result;
50 + }
51 +
52 + @Override
53 + public VirtualNetwork decode(ObjectNode json, CodecContext context) {
54 + if (json == null || !json.isObject()) {
55 + return null;
56 + }
57 +
58 + NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
59 + TenantId tId = TenantId.tenantId(extractMember(TENANT_ID, json));
60 + return new DefaultVirtualNetwork(nId, tId);
61 + }
62 +
63 + private String extractMember(String key, ObjectNode json) {
64 + return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
65 + }
66 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.codec.CodecContext;
20 +import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
22 +import org.onosproject.incubator.net.virtual.NetworkId;
23 +import org.onosproject.incubator.net.virtual.VirtualDevice;
24 +import org.onosproject.incubator.net.virtual.VirtualNetworkService;
25 +import org.onosproject.incubator.net.virtual.VirtualPort;
26 +import org.onosproject.net.DefaultAnnotations;
27 +import org.onosproject.net.DefaultDevice;
28 +import org.onosproject.net.DefaultPort;
29 +import org.onosproject.net.Device;
30 +import org.onosproject.net.DeviceId;
31 +import org.onosproject.net.Port;
32 +import org.onosproject.net.PortNumber;
33 +
34 +import java.util.Set;
35 +
36 +import static com.google.common.base.Preconditions.checkNotNull;
37 +import static org.onlab.util.Tools.nullIsIllegal;
38 +
39 +/**
40 + * Codec for the VirtualPort class.
41 + */
42 +public class VirtualPortCodec extends JsonCodec<VirtualPort> {
43 +
44 + // JSON field names
45 + private static final String NETWORK_ID = "networkId";
46 + private static final String DEVICE_ID = "deviceId";
47 + private static final String PORT_NUM = "portNum";
48 + private static final String PHYS_DEVICE_ID = "physDeviceId";
49 + private static final String PHYS_PORT_NUM = "physPortNum";
50 +
51 + private static final String NULL_OBJECT_MSG = "VirtualPort cannot be null";
52 + private static final String MISSING_MEMBER_MSG = " member is required in VirtualPort";
53 + private static final String INVALID_VIRTUAL_DEVICE = " is not a valid VirtualDevice";
54 +
55 + @Override
56 + public ObjectNode encode(VirtualPort vPort, CodecContext context) {
57 + checkNotNull(vPort, NULL_OBJECT_MSG);
58 +
59 + ObjectNode result = context.mapper().createObjectNode()
60 + .put(NETWORK_ID, vPort.networkId().toString())
61 + .put(DEVICE_ID, vPort.element().id().toString())
62 + .put(PORT_NUM, vPort.number().toString())
63 + .put(PHYS_DEVICE_ID, vPort.realizedBy().element().id().toString())
64 + .put(PHYS_PORT_NUM, vPort.realizedBy().number().toString());
65 +
66 + return result;
67 + }
68 +
69 + @Override
70 + public VirtualPort decode(ObjectNode json, CodecContext context) {
71 + if (json == null || !json.isObject()) {
72 + return null;
73 + }
74 +
75 + NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
76 + DeviceId dId = DeviceId.deviceId(extractMember(DEVICE_ID, json));
77 +
78 + VirtualNetworkService vnetService = context.getService(VirtualNetworkService.class);
79 + Set<VirtualDevice> vDevs = vnetService.getVirtualDevices(nId);
80 + VirtualDevice vDev = vDevs.stream()
81 + .filter(virtualDevice -> virtualDevice.id().equals(dId))
82 + .findFirst().orElse(null);
83 + nullIsIllegal(vDev, dId.toString() + INVALID_VIRTUAL_DEVICE);
84 +
85 + PortNumber portNum = PortNumber.portNumber(extractMember(PORT_NUM, json));
86 + DeviceId physDId = DeviceId.deviceId(extractMember(PHYS_DEVICE_ID, json));
87 + PortNumber physPortNum = PortNumber.portNumber(extractMember(PHYS_PORT_NUM, json));
88 +
89 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
90 + Device physDevice = new DefaultDevice(null, physDId,
91 + null, null, null, null, null, null, annotations);
92 + Port realizedBy = new DefaultPort(physDevice, physPortNum, true);
93 + return new DefaultVirtualPort(nId, vDev, portNum, realizedBy);
94 + }
95 +
96 + private String extractMember(String key, ObjectNode json) {
97 + return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
98 + }
99 +}
...@@ -22,7 +22,7 @@ import org.onosproject.net.Port; ...@@ -22,7 +22,7 @@ import org.onosproject.net.Port;
22 * Representation of a virtual port. 22 * Representation of a virtual port.
23 */ 23 */
24 @Beta 24 @Beta
25 -public interface VirtualPort extends Port { 25 +public interface VirtualPort extends VirtualElement, Port {
26 26
27 /** 27 /**
28 * Returns the underlying port using which this port is realized. 28 * Returns the underlying port using which this port is realized.
......
...@@ -47,7 +47,9 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -47,7 +47,9 @@ public class CoreWebApplication extends AbstractWebApplication {
47 FlowObjectiveWebResource.class, 47 FlowObjectiveWebResource.class,
48 MulticastRouteWebResource.class, 48 MulticastRouteWebResource.class,
49 DeviceKeyWebResource.class, 49 DeviceKeyWebResource.class,
50 - RegionsWebResource.class 50 + RegionsWebResource.class,
51 + TenantWebResource.class,
52 + VirtualNetworkWebResource.class
51 ); 53 );
52 } 54 }
53 } 55 }
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.rest.resources;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import org.onlab.util.ItemNotFoundException;
22 +import org.onosproject.incubator.net.virtual.TenantId;
23 +import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
24 +import org.onosproject.rest.AbstractWebResource;
25 +
26 +import javax.ws.rs.Consumes;
27 +import javax.ws.rs.DELETE;
28 +import javax.ws.rs.GET;
29 +import javax.ws.rs.POST;
30 +import javax.ws.rs.Path;
31 +import javax.ws.rs.PathParam;
32 +import javax.ws.rs.Produces;
33 +import javax.ws.rs.core.Context;
34 +import javax.ws.rs.core.MediaType;
35 +import javax.ws.rs.core.Response;
36 +import javax.ws.rs.core.UriBuilder;
37 +import javax.ws.rs.core.UriInfo;
38 +import java.io.IOException;
39 +import java.io.InputStream;
40 +
41 +/**
42 + * Query and manage tenants of virtual networks.
43 + */
44 +@Path("tenants")
45 +public class TenantWebResource extends AbstractWebResource {
46 +
47 + private static final String MISSING_TENANTID = "Missing tenant identifier";
48 + private static final String TENANTID_NOT_FOUND = "Tenant identifier not found";
49 + private static final String INVALID_TENANTID = "Invalid tenant identifier ";
50 +
51 + @Context
52 + UriInfo uriInfo;
53 +
54 + private final VirtualNetworkAdminService vnetAdminService = get(VirtualNetworkAdminService.class);
55 +
56 + /**
57 + * Returns all tenants.
58 + *
59 + * @return 200 OK
60 + * @onos.rsModel TenantIds
61 + */
62 + @GET
63 + @Produces(MediaType.APPLICATION_JSON)
64 + public Response getVirtualNetworkTenants() {
65 + Iterable<TenantId> tenantIds = vnetAdminService.getTenantIds();
66 + return ok(encodeArray(TenantId.class, "tenants", tenantIds)).build();
67 + }
68 +
69 + /**
70 + * Creates a tenant with the given tenant identifier.
71 + *
72 + * @param stream TenantId JSON stream
73 + * @return status of the request - CREATED if the JSON is correct,
74 + * BAD_REQUEST if the JSON is invalid
75 + * @onos.rsModel TenantId
76 + */
77 + @POST
78 + @Consumes(MediaType.APPLICATION_JSON)
79 + @Produces(MediaType.APPLICATION_JSON)
80 + public Response addTenantId(InputStream stream) {
81 + try {
82 + final TenantId tid = getTenantIdFromJsonStream(stream);
83 + vnetAdminService.registerTenantId(tid);
84 + final TenantId resultTid = getExistingTenantId(vnetAdminService, tid);
85 + UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
86 + .path("tenants")
87 + .path(resultTid.id());
88 + return Response
89 + .created(locationBuilder.build())
90 + .build();
91 + } catch (IOException e) {
92 + throw new IllegalArgumentException(e);
93 + }
94 + }
95 +
96 + /**
97 + * Removes the specified tenant with the specified tenant identifier.
98 + *
99 + * @param tenantId tenant identifier
100 + * @return 200 OK, 404 not found
101 + */
102 + @DELETE
103 + @Path("{tenantId}")
104 + public Response removeTenantId(@PathParam("tenantId") String tenantId) {
105 + final TenantId tid = TenantId.tenantId(tenantId);
106 + final TenantId existingTid = getExistingTenantId(vnetAdminService, tid);
107 + vnetAdminService.unregisterTenantId(existingTid);
108 + return Response.ok().build();
109 + }
110 +
111 + /**
112 + * Removes the specified tenant with the specified tenant identifier.
113 + *
114 + * @param stream deviceIds JSON stream
115 + * @return 200 OK, 404 not found
116 + * @onos.rsModel TenantId
117 + */
118 + @DELETE
119 + public Response removeTenantId(InputStream stream) {
120 + try {
121 + final TenantId tid = getTenantIdFromJsonStream(stream);
122 + vnetAdminService.unregisterTenantId(tid);
123 + } catch (IOException e) {
124 + throw new IllegalArgumentException(e);
125 + }
126 + return Response.ok().build();
127 + }
128 +
129 + /**
130 + * Get the tenant identifier from the JSON stream.
131 + *
132 + * @param stream TenantId JSON stream
133 + * @return TenantId
134 + * @throws IOException
135 + */
136 + private TenantId getTenantIdFromJsonStream(InputStream stream) throws IOException {
137 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
138 + JsonNode specifiedTenantId = jsonTree.get("id");
139 +
140 + if (specifiedTenantId == null) {
141 + throw new IllegalArgumentException(MISSING_TENANTID);
142 + }
143 + return TenantId.tenantId(specifiedTenantId.asText());
144 + }
145 +
146 + /**
147 + * Get the matching tenant identifier from existing tenant identifiers in system.
148 + *
149 + * @param vnetAdminSvc
150 + * @param tidIn tenant identifier
151 + * @return TenantId
152 + */
153 + private static TenantId getExistingTenantId(VirtualNetworkAdminService vnetAdminSvc,
154 + TenantId tidIn) {
155 + final TenantId resultTid = vnetAdminSvc
156 + .getTenantIds()
157 + .stream()
158 + .filter(tenantId -> tenantId.equals(tidIn))
159 + .findFirst()
160 + .orElseThrow(() -> new ItemNotFoundException(TENANTID_NOT_FOUND));
161 + return resultTid;
162 + }
163 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.rest.resources;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import org.onosproject.incubator.net.virtual.NetworkId;
22 +import org.onosproject.incubator.net.virtual.TenantId;
23 +import org.onosproject.incubator.net.virtual.VirtualDevice;
24 +import org.onosproject.incubator.net.virtual.VirtualNetwork;
25 +import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
26 +import org.onosproject.incubator.net.virtual.VirtualNetworkService;
27 +import org.onosproject.incubator.net.virtual.VirtualPort;
28 +import org.onosproject.net.DefaultAnnotations;
29 +import org.onosproject.net.DefaultDevice;
30 +import org.onosproject.net.DefaultPort;
31 +import org.onosproject.net.Device;
32 +import org.onosproject.net.DeviceId;
33 +import org.onosproject.net.Port;
34 +import org.onosproject.net.PortNumber;
35 +import org.onosproject.rest.AbstractWebResource;
36 +
37 +import javax.ws.rs.Consumes;
38 +import javax.ws.rs.DELETE;
39 +import javax.ws.rs.GET;
40 +import javax.ws.rs.POST;
41 +import javax.ws.rs.Path;
42 +import javax.ws.rs.PathParam;
43 +import javax.ws.rs.Produces;
44 +import javax.ws.rs.core.Context;
45 +import javax.ws.rs.core.MediaType;
46 +import javax.ws.rs.core.Response;
47 +import javax.ws.rs.core.UriBuilder;
48 +import javax.ws.rs.core.UriInfo;
49 +import java.io.IOException;
50 +import java.io.InputStream;
51 +import java.util.Collection;
52 +import java.util.List;
53 +import java.util.Set;
54 +import java.util.stream.Collectors;
55 +
56 +/**
57 + * Query and Manage Virtual Network elements.
58 + */
59 +@Path("vnets")
60 +public class VirtualNetworkWebResource extends AbstractWebResource {
61 +
62 + private static final String MISSING_FIELD = "Missing ";
63 + private static final String INVALID_FIELD = "Invalid ";
64 +
65 + private final VirtualNetworkAdminService vnetAdminService = get(VirtualNetworkAdminService.class);
66 + private final VirtualNetworkService vnetService = get(VirtualNetworkService.class);
67 +
68 + @Context
69 + UriInfo uriInfo;
70 +
71 + // VirtualNetwork
72 + // TODO Query vnets by tenant
73 +
74 + /**
75 + * Returns all virtual networks.
76 + *
77 + * @return 200 OK
78 + */
79 + @GET
80 + @Produces(MediaType.APPLICATION_JSON)
81 + public Response getVirtualNetworks() {
82 + Set<TenantId> tenantIds = vnetAdminService.getTenantIds();
83 + List<VirtualNetwork> allVnets = tenantIds.stream()
84 + .map(tenantId -> vnetService.getVirtualNetworks(tenantId))
85 + .flatMap(Collection::stream)
86 + .collect(Collectors.toList());
87 + return ok(encodeArray(VirtualNetwork.class, "vnets", allVnets)).build();
88 + }
89 +
90 + /**
91 + * Creates a virtual network from the JSON input stream.
92 + *
93 + * @param stream TenantId JSON stream
94 + * @return status of the request - CREATED if the JSON is correct,
95 + * BAD_REQUEST if the JSON is invalid
96 + * @onos.rsModel TenantId
97 + */
98 + @POST
99 + @Consumes(MediaType.APPLICATION_JSON)
100 + @Produces(MediaType.APPLICATION_JSON)
101 + public Response createVirtualNetwork(InputStream stream) {
102 + try {
103 + final TenantId tid = TenantId.tenantId(getFromJsonStream(stream, "id").asText());
104 + VirtualNetwork newVnet = vnetAdminService.createVirtualNetwork(tid);
105 + UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
106 + .path("vnets")
107 + .path(newVnet.id().toString());
108 + return Response
109 + .created(locationBuilder.build())
110 + .build();
111 + } catch (IOException e) {
112 + throw new IllegalArgumentException(e);
113 + }
114 + }
115 +
116 + /**
117 + * Removes the virtual network with the specified network identifier.
118 + *
119 + * @param networkId network identifier
120 + * @return 200 OK, 404 not found
121 + */
122 + @DELETE
123 + @Path("{networkId}")
124 + public Response removeVirtualNetwork(@PathParam("networkId") long networkId) {
125 + final NetworkId nid = NetworkId.networkId(networkId);
126 + vnetAdminService.removeVirtualNetwork(nid);
127 + return Response.ok().build();
128 + }
129 +
130 + // VirtualDevice
131 +
132 + /**
133 + * Returns all virtual network devices in a virtual network.
134 + *
135 + * @param networkId network identifier
136 + * @return 200 OK
137 + */
138 + @GET
139 + @Produces(MediaType.APPLICATION_JSON)
140 + @Path("{networkId}/devices")
141 + public Response getVirtualDevices(@PathParam("networkId") long networkId) {
142 + final NetworkId nid = NetworkId.networkId(networkId);
143 + Set<VirtualDevice> vdevs = vnetService.getVirtualDevices(nid);
144 + return ok(encodeArray(VirtualDevice.class, "devices", vdevs)).build();
145 + }
146 +
147 + /**
148 + * Creates a virtual device from the JSON input stream.
149 + *
150 + * @param networkId network identifier
151 + * @param stream Virtual device JSON stream
152 + * @return status of the request - CREATED if the JSON is correct,
153 + * BAD_REQUEST if the JSON is invalid
154 + * @onos.rsModel VirtualDevice
155 + */
156 + @POST
157 + @Path("{networkId}/devices/")
158 + @Consumes(MediaType.APPLICATION_JSON)
159 + @Produces(MediaType.APPLICATION_JSON)
160 + public Response createVirtualDevice(@PathParam("networkId") long networkId,
161 + InputStream stream) {
162 + try {
163 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
164 + final VirtualDevice vdevReq = codec(VirtualDevice.class).decode(jsonTree, this);
165 + JsonNode specifiedRegionId = jsonTree.get("networkId");
166 + if (specifiedRegionId != null &&
167 + specifiedRegionId.asLong() != (networkId)) {
168 + throw new IllegalArgumentException(INVALID_FIELD + "networkId");
169 + }
170 + final VirtualDevice vdevRes = vnetAdminService.createVirtualDevice(vdevReq.networkId(),
171 + vdevReq.id());
172 + UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
173 + .path("vnets").path(specifiedRegionId.asText())
174 + .path("devices").path(vdevRes.id().toString());
175 + return Response
176 + .created(locationBuilder.build())
177 + .build();
178 + } catch (IOException e) {
179 + throw new IllegalArgumentException(e);
180 + }
181 + }
182 +
183 + /**
184 + * Removes the virtual network device from the virtual network.
185 + *
186 + * @param networkId network identifier
187 + * @param deviceId device identifier
188 + * @return 200 OK, 404 not found
189 + */
190 + @DELETE
191 + @Path("{networkId}/devices/{deviceId}")
192 + public Response removeVirtualDevice(@PathParam("networkId") long networkId,
193 + @PathParam("deviceId") String deviceId) {
194 + final NetworkId nid = NetworkId.networkId(networkId);
195 + final DeviceId did = DeviceId.deviceId(deviceId);
196 + vnetAdminService.removeVirtualDevice(nid, did);
197 + return Response.ok().build();
198 + }
199 +
200 + // VirtualPort
201 +
202 + /**
203 + * Returns all virtual network ports in a virtual device in a virtual network.
204 + *
205 + * @param networkId network identifier
206 + * @param deviceId virtual device identifier
207 + * @return 200 OK
208 + */
209 + @GET
210 + @Produces(MediaType.APPLICATION_JSON)
211 + @Path("{networkId}/devices/{deviceId}/ports")
212 + public Response getVirtualPorts(@PathParam("networkId") long networkId,
213 + @PathParam("deviceId") String deviceId) {
214 + final NetworkId nid = NetworkId.networkId(networkId);
215 + Iterable<VirtualPort> vports = vnetService.getVirtualPorts(nid, DeviceId.deviceId(deviceId));
216 + return ok(encodeArray(VirtualPort.class, "ports", vports)).build();
217 + }
218 +
219 + /**
220 + * Creates a virtual network port in a virtual device in a virtual network.
221 + *
222 + * @param networkId network identifier
223 + * @param virtDeviceId virtual device identifier
224 + * @param stream Virtual device JSON stream
225 + * @return status of the request - CREATED if the JSON is correct,
226 + * BAD_REQUEST if the JSON is invalid
227 + * @onos.rsModel VirtualPort
228 + */
229 + @POST
230 + @Consumes(MediaType.APPLICATION_JSON)
231 + @Produces(MediaType.APPLICATION_JSON)
232 + @Path("{networkId}/devices/{deviceId}/ports")
233 + public Response createVirtualPort(@PathParam("networkId") long networkId,
234 + @PathParam("deviceId") String virtDeviceId,
235 + InputStream stream) {
236 + try {
237 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
238 +// final VirtualPort vportReq = codec(VirtualPort.class).decode(jsonTree, this);
239 + JsonNode specifiedNetworkId = jsonTree.get("networkId");
240 + JsonNode specifiedDeviceId = jsonTree.get("deviceId");
241 + if (specifiedNetworkId != null &&
242 + specifiedNetworkId.asLong() != (networkId)) {
243 + throw new IllegalArgumentException(INVALID_FIELD + "networkId");
244 + }
245 + if (specifiedDeviceId != null &&
246 + !specifiedDeviceId.asText().equals(virtDeviceId)) {
247 + throw new IllegalArgumentException(INVALID_FIELD + "deviceId");
248 + }
249 + JsonNode specifiedPortNum = jsonTree.get("portNum");
250 + JsonNode specifiedPhysDeviceId = jsonTree.get("physDeviceId");
251 + JsonNode specifiedPhysPortNum = jsonTree.get("physPortNum");
252 + final NetworkId nid = NetworkId.networkId(networkId);
253 + DeviceId vdevId = DeviceId.deviceId(virtDeviceId);
254 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
255 + Device physDevice = new DefaultDevice(null, DeviceId.deviceId(specifiedPhysDeviceId.asText()),
256 + null, null, null, null, null, null, annotations);
257 + Port realizedBy = new DefaultPort(physDevice,
258 + PortNumber.portNumber(specifiedPhysPortNum.asText()), true);
259 + VirtualPort vport = vnetAdminService.createVirtualPort(nid, vdevId,
260 + PortNumber.portNumber(specifiedPortNum.asText()), realizedBy);
261 + UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
262 + .path("vnets").path(specifiedNetworkId.asText())
263 + .path("devices").path(specifiedDeviceId.asText())
264 + .path("ports").path(vport.number().toString());
265 + return Response
266 + .created(locationBuilder.build())
267 + .build();
268 + } catch (IOException e) {
269 + throw new IllegalArgumentException(e);
270 + }
271 + }
272 +
273 + /**
274 + * Removes the virtual network port from the virtual device in a virtual network.
275 + *
276 + * @param networkId network identifier
277 + * @param deviceId virtual device identifier
278 + * @param portNum virtual port number
279 + * @return 200 OK, 404 not found
280 + */
281 + @DELETE
282 + @Path("{networkId}/devices/{deviceId}/ports/{portNum}")
283 + public Response removeVirtualPort(@PathParam("networkId") long networkId,
284 + @PathParam("deviceId") String deviceId,
285 + @PathParam("portNum") long portNum) {
286 + final NetworkId nid = NetworkId.networkId(networkId);
287 + vnetAdminService.removeVirtualPort(nid, DeviceId.deviceId(deviceId),
288 + PortNumber.portNumber(portNum));
289 + return Response.ok().build();
290 + }
291 +
292 + // TODO VirtualLink
293 +
294 + /**
295 + * Get the tenant identifier from the JSON stream.
296 + *
297 + * @param stream TenantId JSON stream
298 + * @param jsonFieldName field name
299 + * @return JsonNode
300 + * @throws IOException
301 + */
302 + private JsonNode getFromJsonStream(InputStream stream, String jsonFieldName) throws IOException {
303 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
304 + JsonNode jsonNode = jsonTree.get(jsonFieldName);
305 +
306 + if (jsonNode == null) {
307 + throw new IllegalArgumentException(MISSING_FIELD + jsonFieldName);
308 + }
309 + return jsonNode;
310 + }
311 +}
1 +{
2 + "type": "object",
3 + "title": "TenantId",
4 + "required": [
5 + "id"
6 + ],
7 + "properties": {
8 + "id": {
9 + "type": "String",
10 + "example": "Tenant unique identifier"
11 + }
12 + }
13 +}
1 +{
2 + "type": "object",
3 + "title": "tenants",
4 + "required": [
5 + "tenants"
6 + ],
7 + "properties": {
8 + "tenants": {
9 + "type": "array",
10 + "xml": {
11 + "name": "tenants",
12 + "wrapped": true
13 + },
14 + "items": {
15 + "type": "object",
16 + "title": "tenant",
17 + "required": [
18 + "id"
19 + ],
20 + "properties": {
21 + "id": {
22 + "type": "String",
23 + "example": "Tenant unique identifier"
24 + }
25 + }
26 + }
27 + }
28 + }
29 +}
1 +{
2 + "type": "object",
3 + "title": "vdev",
4 + "required": [
5 + "networkId",
6 + "deviceId"
7 + ],
8 + "properties": {
9 + "networkId": {
10 + "type": "String",
11 + "example": "Network identifier"
12 + },
13 + "deviceId": {
14 + "type": "String",
15 + "example": "Device identifier"
16 + }
17 + }
18 +}
1 +{
2 + "type": "object",
3 + "title": "vport",
4 + "required": [
5 + "networkId",
6 + "deviceId",
7 + "portNum",
8 + "physDeviceId",
9 + "physPortNum"
10 + ],
11 + "properties": {
12 + "networkId": {
13 + "type": "String",
14 + "example": "Network identifier"
15 + },
16 + "deviceId": {
17 + "type": "String",
18 + "example": "Virtual device identifier"
19 + },
20 + "portNum": {
21 + "type": "String",
22 + "example": "Virtual device port number"
23 + },
24 + "physDeviceId": {
25 + "type": "String",
26 + "example": "Physical device identifier"
27 + },
28 + "physPortNum": {
29 + "type": "String",
30 + "example": "Physical device port number"
31 + }
32 + }
33 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.rest.resources;
18 +
19 +import com.eclipsesource.json.Json;
20 +import com.eclipsesource.json.JsonArray;
21 +import com.eclipsesource.json.JsonObject;
22 +import com.google.common.collect.ImmutableSet;
23 +import org.glassfish.jersey.client.ClientProperties;
24 +import org.hamcrest.Description;
25 +import org.hamcrest.Matchers;
26 +import org.hamcrest.TypeSafeMatcher;
27 +import org.junit.Before;
28 +import org.junit.Test;
29 +import org.onlab.osgi.ServiceDirectory;
30 +import org.onlab.osgi.TestServiceDirectory;
31 +import org.onlab.rest.BaseResource;
32 +import org.onosproject.codec.CodecService;
33 +import org.onosproject.codec.impl.CodecManager;
34 +import org.onosproject.incubator.net.virtual.TenantId;
35 +import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
36 +
37 +import javax.ws.rs.BadRequestException;
38 +import javax.ws.rs.NotFoundException;
39 +import javax.ws.rs.client.Entity;
40 +import javax.ws.rs.client.WebTarget;
41 +import javax.ws.rs.core.MediaType;
42 +import javax.ws.rs.core.Response;
43 +import java.io.InputStream;
44 +import java.net.HttpURLConnection;
45 +import java.util.HashSet;
46 +
47 +import static org.easymock.EasyMock.*;
48 +import static org.hamcrest.Matchers.*;
49 +import static org.junit.Assert.*;
50 +
51 +/**
52 + * Unit tests for tenant REST APIs.
53 + */
54 +public class TenantWebResourceTest extends ResourceTest {
55 +
56 + private final VirtualNetworkAdminService mockVnetAdminService = createMock(VirtualNetworkAdminService.class);
57 +
58 + final HashSet<TenantId> tenantIdSet = new HashSet<>();
59 +
60 + private static final String ID = "id";
61 +
62 + private final TenantId tenantId1 = TenantId.tenantId("TenantId1");
63 + private final TenantId tenantId2 = TenantId.tenantId("TenantId2");
64 + private final TenantId tenantId3 = TenantId.tenantId("TenantId3");
65 + private final TenantId tenantId4 = TenantId.tenantId("TenantId4");
66 +
67 + /**
68 + * Sets up the global values for all the tests.
69 + */
70 + @Before
71 + public void setUpTest() {
72 + // Register the services needed for the test
73 + CodecManager codecService = new CodecManager();
74 + codecService.activate();
75 + ServiceDirectory testDirectory =
76 + new TestServiceDirectory()
77 + .add(VirtualNetworkAdminService.class, mockVnetAdminService)
78 + .add(CodecService.class, codecService);
79 +
80 + BaseResource.setServiceDirectory(testDirectory);
81 + }
82 +
83 + /**
84 + * Hamcrest matcher to check that a tenant id representation in JSON matches
85 + * the actual tenant id.
86 + */
87 + public static class TenantIdJsonMatcher extends TypeSafeMatcher<JsonObject> {
88 + private final TenantId tenantId;
89 + private String reason = "";
90 +
91 + public TenantIdJsonMatcher(TenantId tenantIdValue) {
92 + tenantId = tenantIdValue;
93 + }
94 +
95 + @Override
96 + public boolean matchesSafely(JsonObject jsonHost) {
97 + // Check the tenant id
98 + final String jsonId = jsonHost.get(ID).asString();
99 + if (!jsonId.equals(tenantId.id())) {
100 + reason = ID + " " + tenantId.id();
101 + return false;
102 + }
103 +
104 + return true;
105 + }
106 +
107 + @Override
108 + public void describeTo(Description description) {
109 + description.appendText(reason);
110 + }
111 + }
112 +
113 + /**
114 + * Factory to allocate a tenant id array matcher.
115 + *
116 + * @param tenantId tenant id object we are looking for
117 + * @return matcher
118 + */
119 + private static TenantIdJsonMatcher matchesTenantId(TenantId tenantId) {
120 + return new TenantIdJsonMatcher(tenantId);
121 + }
122 +
123 + /**
124 + * Hamcrest matcher to check that a tenant id is represented properly in a JSON
125 + * array of tenant ids.
126 + */
127 + public static class TenantIdJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
128 + private final TenantId tenantId;
129 + private String reason = "";
130 +
131 + public TenantIdJsonArrayMatcher(TenantId tenantIdValue) {
132 + tenantId = tenantIdValue;
133 + }
134 +
135 + @Override
136 + public boolean matchesSafely(JsonArray json) {
137 + boolean tenantIdFound = false;
138 + final int expectedAttributes = 1;
139 + for (int tenantIdIndex = 0; tenantIdIndex < json.size();
140 + tenantIdIndex++) {
141 +
142 + final JsonObject jsonHost = json.get(tenantIdIndex).asObject();
143 +
144 + // Only 1 attribute - ID.
145 + if (jsonHost.names().size() < expectedAttributes) {
146 + reason = "Found a tenant id with the wrong number of attributes";
147 + return false;
148 + }
149 +
150 + final String jsonDeviceKeyId = jsonHost.get(ID).asString();
151 + if (jsonDeviceKeyId.equals(tenantId.id())) {
152 + tenantIdFound = true;
153 +
154 + // We found the correct tenant id, check the tenant id attribute values
155 + assertThat(jsonHost, matchesTenantId(tenantId));
156 + }
157 + }
158 + if (!tenantIdFound) {
159 + reason = "Tenant id " + tenantId.id() + " was not found";
160 + return false;
161 + }
162 + return true;
163 + }
164 +
165 + @Override
166 + public void describeTo(Description description) {
167 + description.appendText(reason);
168 + }
169 + }
170 +
171 + /**
172 + * Factory to allocate a tenant id array matcher.
173 + *
174 + * @param tenantId tenant id object we are looking for
175 + * @return matcher
176 + */
177 + private static TenantIdJsonArrayMatcher hasTenantId(TenantId tenantId) {
178 + return new TenantIdJsonArrayMatcher(tenantId);
179 + }
180 +
181 + /**
182 + * Tests the result of the REST API GET when there are no tenant ids.
183 + */
184 + @Test
185 + public void testGetTenantsEmptyArray() {
186 + expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of()).anyTimes();
187 + replay(mockVnetAdminService);
188 +
189 + WebTarget wt = target();
190 + String response = wt.path("tenants").request().get(String.class);
191 + assertThat(response, is("{\"tenants\":[]}"));
192 +
193 + verify(mockVnetAdminService);
194 + }
195 +
196 + /**
197 + * Tests the result of the REST API GET when tenant ids are defined.
198 + */
199 + @Test
200 + public void testGetTenantIdsArray() {
201 + tenantIdSet.add(tenantId1);
202 + tenantIdSet.add(tenantId2);
203 + tenantIdSet.add(tenantId3);
204 + tenantIdSet.add(tenantId4);
205 + expect(mockVnetAdminService.getTenantIds()).andReturn(tenantIdSet).anyTimes();
206 + replay(mockVnetAdminService);
207 +
208 + WebTarget wt = target();
209 + String response = wt.path("tenants").request().get(String.class);
210 + assertThat(response, containsString("{\"tenants\":["));
211 +
212 + final JsonObject result = Json.parse(response).asObject();
213 + assertThat(result, notNullValue());
214 +
215 + assertThat(result.names(), hasSize(1));
216 + assertThat(result.names().get(0), is("tenants"));
217 +
218 + final JsonArray tenantIds = result.get("tenants").asArray();
219 + assertThat(tenantIds, notNullValue());
220 + assertEquals("Device keys array is not the correct size.",
221 + tenantIdSet.size(), tenantIds.size());
222 +
223 + tenantIdSet.forEach(tenantId -> assertThat(tenantIds, hasTenantId(tenantId)));
224 +
225 + verify(mockVnetAdminService);
226 + }
227 +
228 + /**
229 + * Tests adding of new tenant id using POST via JSON stream.
230 + */
231 + @Test
232 + public void testPost() {
233 + mockVnetAdminService.registerTenantId(anyObject());
234 + tenantIdSet.add(tenantId2);
235 + expect(mockVnetAdminService.getTenantIds()).andReturn(tenantIdSet).anyTimes();
236 + expectLastCall();
237 +
238 + replay(mockVnetAdminService);
239 +
240 + WebTarget wt = target();
241 + InputStream jsonStream = TenantWebResourceTest.class
242 + .getResourceAsStream("post-tenant.json");
243 +
244 + Response response = wt.path("tenants").request(MediaType.APPLICATION_JSON_TYPE)
245 + .post(Entity.json(jsonStream));
246 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
247 +
248 + String location = response.getLocation().getPath();
249 + assertThat(location, Matchers.startsWith("/tenants/" + tenantId2));
250 +
251 + verify(mockVnetAdminService);
252 + }
253 +
254 + /**
255 + * Tests adding of a null tenant id using POST via JSON stream.
256 + */
257 + @Test
258 + public void testPostNullTenantId() {
259 +
260 + replay(mockVnetAdminService);
261 +
262 + WebTarget wt = target();
263 + try {
264 + String response = wt.path("tenants")
265 + .request(MediaType.APPLICATION_JSON_TYPE)
266 + .post(Entity.json(null), String.class);
267 + fail("POST of null tenant id did not throw an exception");
268 + } catch (BadRequestException ex) {
269 + assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
270 + }
271 +
272 + verify(mockVnetAdminService);
273 + }
274 +
275 + /**
276 + * Tests removing a tenant id with DELETE request.
277 + */
278 + @Test
279 + public void testDelete() {
280 + expect(mockVnetAdminService.getTenantIds())
281 + .andReturn(ImmutableSet.of(tenantId2)).anyTimes();
282 + mockVnetAdminService.unregisterTenantId(anyObject());
283 + expectLastCall();
284 + replay(mockVnetAdminService);
285 +
286 + WebTarget wt = target()
287 + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
288 + Response response = wt.path("tenants/" + tenantId2)
289 + .request(MediaType.APPLICATION_JSON_TYPE)
290 + .delete();
291 +
292 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
293 +
294 + verify(mockVnetAdminService);
295 + }
296 +
297 + /**
298 + * Tests removing a tenant id with DELETE request via JSON stream.
299 + */
300 + @Test
301 + public void testDeleteViaJson() {
302 + mockVnetAdminService.unregisterTenantId(anyObject());
303 + expectLastCall();
304 + replay(mockVnetAdminService);
305 +
306 + WebTarget wt = target()
307 + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
308 + InputStream jsonStream = TenantWebResourceTest.class
309 + .getResourceAsStream("post-tenant.json");
310 + Response response = wt.request().method("DELETE", Entity.json(jsonStream));
311 +
312 +// assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
313 +
314 +// verify(mockVnetAdminService);
315 + }
316 +
317 + /**
318 + * Tests that a DELETE of a non-existent tenant id throws an exception.
319 + */
320 + @Test
321 + public void testDeleteNonExistentDeviceKey() {
322 + expect(mockVnetAdminService.getTenantIds())
323 + .andReturn(ImmutableSet.of())
324 + .anyTimes();
325 + expectLastCall();
326 +
327 + replay(mockVnetAdminService);
328 +
329 + WebTarget wt = target();
330 +
331 + try {
332 + wt.path("tenants/" + "NON_EXISTENT_TENANT_ID")
333 + .request()
334 + .delete(String.class);
335 + fail("Delete of a non-existent tenant did not throw an exception");
336 + } catch (NotFoundException ex) {
337 + assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
338 + }
339 +
340 + verify(mockVnetAdminService);
341 + }
342 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.rest.resources;
18 +
19 +import com.eclipsesource.json.Json;
20 +import com.eclipsesource.json.JsonArray;
21 +import com.eclipsesource.json.JsonObject;
22 +import com.google.common.collect.ImmutableList;
23 +import com.google.common.collect.ImmutableSet;
24 +import org.glassfish.jersey.client.ClientProperties;
25 +import org.hamcrest.Description;
26 +import org.hamcrest.Matchers;
27 +import org.hamcrest.TypeSafeMatcher;
28 +import org.junit.Before;
29 +import org.junit.Test;
30 +import org.onlab.osgi.ServiceDirectory;
31 +import org.onlab.osgi.TestServiceDirectory;
32 +import org.onlab.rest.BaseResource;
33 +import org.onosproject.codec.CodecService;
34 +import org.onosproject.codec.impl.CodecManager;
35 +import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
36 +import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
37 +import org.onosproject.incubator.net.virtual.DefaultVirtualPort;
38 +import org.onosproject.incubator.net.virtual.NetworkId;
39 +import org.onosproject.incubator.net.virtual.TenantId;
40 +import org.onosproject.incubator.net.virtual.VirtualDevice;
41 +import org.onosproject.incubator.net.virtual.VirtualNetwork;
42 +import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
43 +import org.onosproject.incubator.net.virtual.VirtualNetworkService;
44 +import org.onosproject.incubator.net.virtual.VirtualPort;
45 +import org.onosproject.net.DefaultAnnotations;
46 +import org.onosproject.net.DefaultDevice;
47 +import org.onosproject.net.DefaultPort;
48 +import org.onosproject.net.Device;
49 +import org.onosproject.net.DeviceId;
50 +import org.onosproject.net.NetTestTools;
51 +import org.onosproject.net.Port;
52 +import org.onosproject.net.PortNumber;
53 +
54 +import javax.ws.rs.BadRequestException;
55 +import javax.ws.rs.NotFoundException;
56 +import javax.ws.rs.client.Entity;
57 +import javax.ws.rs.client.WebTarget;
58 +import javax.ws.rs.core.MediaType;
59 +import javax.ws.rs.core.Response;
60 +import java.io.InputStream;
61 +import java.net.HttpURLConnection;
62 +import java.util.HashSet;
63 +import java.util.List;
64 +import java.util.function.BiFunction;
65 +import java.util.function.BiPredicate;
66 +import java.util.function.Function;
67 +
68 +import static org.easymock.EasyMock.*;
69 +import static org.hamcrest.Matchers.*;
70 +import static org.junit.Assert.*;
71 +import static org.onosproject.net.PortNumber.portNumber;
72 +
73 +/**
74 + * Unit tests for virtual network REST APIs.
75 + */
76 +public class VirtualNetworkWebResourceTest extends ResourceTest {
77 +
78 + private final VirtualNetworkAdminService mockVnetAdminService = createMock(VirtualNetworkAdminService.class);
79 + private final VirtualNetworkService mockVnetService = createMock(VirtualNetworkService.class);
80 + private CodecManager codecService;
81 +
82 + final HashSet<TenantId> tenantIdSet = new HashSet<>();
83 + final HashSet<VirtualNetwork> vnetSet = new HashSet<>();
84 + final HashSet<VirtualDevice> vdevSet = new HashSet<>();
85 + final HashSet<VirtualPort> vportSet = new HashSet<>();
86 +
87 + private static final String ID = "networkId";
88 + private static final String TENANT_ID = "tenantId";
89 + private static final String DEVICE_ID = "deviceId";
90 + private static final String PORT_NUM = "portNum";
91 + private static final String PHYS_DEVICE_ID = "physDeviceId";
92 + private static final String PHYS_PORT_NUM = "physPortNum";
93 +
94 + private final TenantId tenantId1 = TenantId.tenantId("TenantId1");
95 + private final TenantId tenantId2 = TenantId.tenantId("TenantId2");
96 + private final TenantId tenantId3 = TenantId.tenantId("TenantId3");
97 + private final TenantId tenantId4 = TenantId.tenantId("TenantId4");
98 +
99 + private final NetworkId networkId1 = NetworkId.networkId(1);
100 + private final NetworkId networkId2 = NetworkId.networkId(2);
101 + private final NetworkId networkId3 = NetworkId.networkId(3);
102 + private final NetworkId networkId4 = NetworkId.networkId(4);
103 +
104 + private final VirtualNetwork vnet1 = new DefaultVirtualNetwork(networkId1, tenantId3);
105 + private final VirtualNetwork vnet2 = new DefaultVirtualNetwork(networkId2, tenantId3);
106 + private final VirtualNetwork vnet3 = new DefaultVirtualNetwork(networkId3, tenantId3);
107 + private final VirtualNetwork vnet4 = new DefaultVirtualNetwork(networkId4, tenantId3);
108 +
109 + private final DeviceId devId1 = DeviceId.deviceId("devId1");
110 + private final DeviceId devId2 = DeviceId.deviceId("devId2");
111 + private final DeviceId devId22 = DeviceId.deviceId("dev22");
112 +
113 + private final VirtualDevice vdev1 = new DefaultVirtualDevice(networkId3, devId1);
114 + private final VirtualDevice vdev2 = new DefaultVirtualDevice(networkId3, devId2);
115 +
116 + private final Device dev1 = NetTestTools.device("dev1");
117 + private final Device dev2 = NetTestTools.device("dev2");
118 + private final Device dev22 = NetTestTools.device("dev22");
119 +
120 + Port port1 = new DefaultPort(dev1, portNumber(1), true);
121 + Port port2 = new DefaultPort(dev2, portNumber(2), true);
122 +
123 + private final VirtualPort vport22 = new DefaultVirtualPort(networkId3,
124 + dev22, portNumber(22), port1);
125 + private final VirtualPort vport23 = new DefaultVirtualPort(networkId3,
126 + dev22, portNumber(23), port2);
127 +
128 + /**
129 + * Sets up the global values for all the tests.
130 + */
131 + @Before
132 + public void setUpTest() {
133 + // Register the services needed for the test
134 + codecService = new CodecManager();
135 + codecService.activate();
136 + ServiceDirectory testDirectory =
137 + new TestServiceDirectory()
138 + .add(VirtualNetworkAdminService.class, mockVnetAdminService)
139 + .add(VirtualNetworkService.class, mockVnetService)
140 + .add(CodecService.class, codecService);
141 +
142 + BaseResource.setServiceDirectory(testDirectory);
143 + }
144 +
145 + /**
146 + * Hamcrest matcher to check that a virtual network entity representation in JSON matches
147 + * the actual virtual network entity.
148 + */
149 + public static class JsonObjectMatcher<T> extends TypeSafeMatcher<JsonObject> {
150 + private final T vnetEntity;
151 + private List<String> jsonFieldNames;
152 + private String reason = "";
153 + private BiFunction<T, String, String> getValue; // get vnetEntity's value
154 +
155 + public JsonObjectMatcher(T vnetEntityValue,
156 + List<String> jsonFieldNames1,
157 + BiFunction<T, String, String> getValue1) {
158 + vnetEntity = vnetEntityValue;
159 + jsonFieldNames = jsonFieldNames1;
160 + getValue = getValue1;
161 + }
162 +
163 + @Override
164 + public boolean matchesSafely(JsonObject jsonHost) {
165 + return jsonFieldNames
166 + .stream()
167 + .allMatch(s -> checkField(jsonHost, s, getValue.apply(vnetEntity, s)));
168 + }
169 +
170 + private boolean checkField(JsonObject jsonHost, String jsonFieldName,
171 + String objectValue) {
172 + final String jsonValue = jsonHost.get(jsonFieldName).asString();
173 + if (!jsonValue.equals(objectValue)) {
174 + reason = jsonFieldName + " " + objectValue;
175 + return false;
176 + }
177 + return true;
178 + }
179 +
180 + @Override
181 + public void describeTo(Description description) {
182 + description.appendText(reason);
183 + }
184 + }
185 +
186 + /**
187 + * Factory to allocate a virtual network id array matcher.
188 + *
189 + * @param obj virtual network id object we are looking for
190 + * @return matcher
191 + */
192 + /**
193 + * Factory to allocate a virtual network entity matcher.
194 + *
195 + * @param obj virtual network object we are looking for
196 + * @param jsonFieldNames JSON field names to check against
197 + * @param getValue function to retrieve value from virtual network object
198 + * @param <T>
199 + * @return JsonObjectMatcher
200 + */
201 + private static <T> JsonObjectMatcher matchesVnetEntity(T obj, List<String> jsonFieldNames,
202 + BiFunction<T, String, String> getValue) {
203 + return new JsonObjectMatcher(obj, jsonFieldNames, getValue);
204 + }
205 +
206 + /**
207 + * Hamcrest matcher to check that a virtual network entity is represented properly in a JSON
208 + * array of virtual network entities.
209 + */
210 + public static class JsonArrayMatcher<T> extends TypeSafeMatcher<JsonArray> {
211 + private final T vnetEntity;
212 + private String reason = "";
213 + private Function<T, String> getKey; // gets vnetEntity's key
214 + private BiPredicate<T, JsonObject> checkKey; // check vnetEntity's key with JSON rep'n
215 + private List<String> jsonFieldNames; // field/property names
216 + private BiFunction<T, String, String> getValue; // get vnetEntity's value
217 +
218 + public JsonArrayMatcher(T vnetEntityValue, Function<T, String> getKey1,
219 + BiPredicate<T, JsonObject> checkKey1,
220 + List<String> jsonFieldNames1,
221 + BiFunction<T, String, String> getValue1) {
222 + vnetEntity = vnetEntityValue;
223 + getKey = getKey1;
224 + checkKey = checkKey1;
225 + jsonFieldNames = jsonFieldNames1;
226 + getValue = getValue1;
227 + }
228 +
229 + @Override
230 + public boolean matchesSafely(JsonArray json) {
231 + boolean itemFound = false;
232 + final int expectedAttributes = jsonFieldNames.size();
233 + for (int jsonArrayIndex = 0; jsonArrayIndex < json.size();
234 + jsonArrayIndex++) {
235 +
236 + final JsonObject jsonHost = json.get(jsonArrayIndex).asObject();
237 +
238 + if (jsonHost.names().size() < expectedAttributes) {
239 + reason = "Found a virtual network with the wrong number of attributes";
240 + return false;
241 + }
242 +
243 + if (checkKey != null && checkKey.test(vnetEntity, jsonHost)) {
244 + itemFound = true;
245 + assertThat(jsonHost, matchesVnetEntity(vnetEntity, jsonFieldNames, getValue));
246 + }
247 + }
248 + if (!itemFound) {
249 + reason = getKey.apply(vnetEntity) + " was not found";
250 + return false;
251 + }
252 + return true;
253 + }
254 +
255 + @Override
256 + public void describeTo(Description description) {
257 + description.appendText(reason);
258 + }
259 + }
260 +
261 + /**
262 + * Array matcher for VirtualNetwork.
263 + */
264 + public static class VnetJsonArrayMatcher extends JsonArrayMatcher<VirtualNetwork> {
265 +
266 + public VnetJsonArrayMatcher(VirtualNetwork vnetIn) {
267 + super(vnetIn,
268 + vnet -> "Virtual network " + vnet.id().toString(),
269 + (vnet, jsonObject) -> {
270 + return jsonObject.get(ID).asString().equals(vnet.id().toString()); },
271 + ImmutableList.of(ID, TENANT_ID),
272 + (vnet, s) -> {
273 + return s.equals(ID) ? vnet.id().toString()
274 + : s.equals(TENANT_ID) ? vnet.tenantId().toString()
275 + : null;
276 + }
277 + );
278 + }
279 + }
280 +
281 + /**
282 + * Factory to allocate a virtual network array matcher.
283 + *
284 + * @param vnet virtual network object we are looking for
285 + * @return matcher
286 + */
287 + private VnetJsonArrayMatcher hasVnet(VirtualNetwork vnet) {
288 + return new VnetJsonArrayMatcher(vnet);
289 + }
290 +
291 + // Tests for Virtual Networks
292 +
293 + /**
294 + * Tests the result of the REST API GET when there are no virtual networks.
295 + */
296 + @Test
297 + public void testGetVirtualNetworksEmptyArray() {
298 + expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of()).anyTimes();
299 + replay(mockVnetAdminService);
300 + expect(mockVnetService.getVirtualNetworks(tenantId4)).andReturn(ImmutableSet.of()).anyTimes();
301 + replay(mockVnetService);
302 +
303 + WebTarget wt = target();
304 + String response = wt.path("vnets").request().get(String.class);
305 + assertThat(response, is("{\"vnets\":[]}"));
306 +
307 + verify(mockVnetService);
308 + verify(mockVnetAdminService);
309 + }
310 +
311 + /**
312 + * Tests the result of the REST API GET when virtual networks are defined.
313 + */
314 + @Test
315 + public void testGetVirtualNetworksArray() {
316 + expect(mockVnetAdminService.getTenantIds()).andReturn(ImmutableSet.of(tenantId3)).anyTimes();
317 + replay(mockVnetAdminService);
318 + vnetSet.add(vnet1);
319 + vnetSet.add(vnet2);
320 + vnetSet.add(vnet3);
321 + vnetSet.add(vnet4);
322 + expect(mockVnetService.getVirtualNetworks(tenantId3)).andReturn(vnetSet).anyTimes();
323 + replay(mockVnetService);
324 +
325 + WebTarget wt = target();
326 + String response = wt.path("vnets").request().get(String.class);
327 + assertThat(response, containsString("{\"vnets\":["));
328 +
329 + final JsonObject result = Json.parse(response).asObject();
330 + assertThat(result, notNullValue());
331 +
332 + assertThat(result.names(), hasSize(1));
333 + assertThat(result.names().get(0), is("vnets"));
334 +
335 + final JsonArray vnetJsonArray = result.get("vnets").asArray();
336 + assertThat(vnetJsonArray, notNullValue());
337 + assertEquals("Virtual networks array is not the correct size.",
338 + vnetSet.size(), vnetJsonArray.size());
339 +
340 + vnetSet.forEach(vnet -> assertThat(vnetJsonArray, hasVnet(vnet)));
341 +
342 + verify(mockVnetService);
343 + verify(mockVnetAdminService);
344 + }
345 +
346 + /**
347 + * Tests adding of new virtual network using POST via JSON stream.
348 + */
349 + @Test
350 + public void testPostVirtualNetwork() {
351 + expect(mockVnetAdminService.createVirtualNetwork(tenantId2)).andReturn(vnet1);
352 + expectLastCall();
353 +
354 + replay(mockVnetAdminService);
355 +
356 + WebTarget wt = target();
357 + InputStream jsonStream = TenantWebResourceTest.class
358 + .getResourceAsStream("post-tenant.json");
359 +
360 + Response response = wt.path("vnets").request(MediaType.APPLICATION_JSON_TYPE)
361 + .post(Entity.json(jsonStream));
362 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
363 +
364 + String location = response.getLocation().getPath();
365 + assertThat(location, Matchers.startsWith("/vnets/" + vnet1.id().toString()));
366 +
367 + verify(mockVnetAdminService);
368 + }
369 +
370 + /**
371 + * Tests adding of a null virtual network using POST via JSON stream.
372 + */
373 + @Test
374 + public void testPostVirtualNetworkNullTenantId() {
375 +
376 + replay(mockVnetAdminService);
377 +
378 + WebTarget wt = target();
379 + try {
380 + String response = wt.path("vnets")
381 + .request(MediaType.APPLICATION_JSON_TYPE)
382 + .post(Entity.json(null), String.class);
383 + fail("POST of null virtual network did not throw an exception");
384 + } catch (BadRequestException ex) {
385 + assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
386 + }
387 +
388 + verify(mockVnetAdminService);
389 + }
390 +
391 + /**
392 + * Tests removing a virtual network with DELETE request.
393 + */
394 + @Test
395 + public void testDeleteVirtualNetwork() {
396 + mockVnetAdminService.removeVirtualNetwork(anyObject());
397 + expectLastCall();
398 + replay(mockVnetAdminService);
399 +
400 + WebTarget wt = target()
401 + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
402 + Response response = wt.path("vnets/" + "2")
403 + .request(MediaType.APPLICATION_JSON_TYPE)
404 + .delete();
405 +
406 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
407 +
408 + verify(mockVnetAdminService);
409 + }
410 +
411 + /**
412 + * Tests that a DELETE of a non-existent virtual network throws an exception.
413 + */
414 + @Test
415 + public void testDeleteNetworkNonExistentNetworkId() {
416 + expect(mockVnetAdminService.getTenantIds())
417 + .andReturn(ImmutableSet.of())
418 + .anyTimes();
419 + expectLastCall();
420 +
421 + replay(mockVnetAdminService);
422 +
423 + WebTarget wt = target();
424 +
425 + try {
426 + wt.path("vnets/" + "NON_EXISTENT_NETWORK_ID")
427 + .request()
428 + .delete(String.class);
429 + fail("Delete of a non-existent virtual network did not throw an exception");
430 + } catch (NotFoundException ex) {
431 + assertThat(ex.getMessage(), containsString("HTTP 404 Not Found"));
432 + }
433 +
434 + verify(mockVnetAdminService);
435 + }
436 +
437 + // Tests for Virtual Device
438 +
439 + /**
440 + * Tests the result of the REST API GET when there are no virtual devices.
441 + */
442 + @Test
443 + public void testGetVirtualDevicesEmptyArray() {
444 + NetworkId networkId = networkId4;
445 + expect(mockVnetService.getVirtualDevices(networkId)).andReturn(ImmutableSet.of()).anyTimes();
446 + replay(mockVnetService);
447 +
448 + WebTarget wt = target();
449 + String location = "vnets/" + networkId.toString() + "/devices";
450 + String response = wt.path(location).request().get(String.class);
451 + assertThat(response, is("{\"devices\":[]}"));
452 +
453 + verify(mockVnetService);
454 + }
455 +
456 + /**
457 + * Tests the result of the REST API GET when virtual devices are defined.
458 + */
459 + @Test
460 + public void testGetVirtualDevicesArray() {
461 + NetworkId networkId = networkId3;
462 + vdevSet.add(vdev1);
463 + vdevSet.add(vdev2);
464 + expect(mockVnetService.getVirtualDevices(networkId)).andReturn(vdevSet).anyTimes();
465 + replay(mockVnetService);
466 +
467 + WebTarget wt = target();
468 + String location = "vnets/" + networkId.toString() + "/devices";
469 + String response = wt.path(location).request().get(String.class);
470 + assertThat(response, containsString("{\"devices\":["));
471 +
472 + final JsonObject result = Json.parse(response).asObject();
473 + assertThat(result, notNullValue());
474 +
475 + assertThat(result.names(), hasSize(1));
476 + assertThat(result.names().get(0), is("devices"));
477 +
478 + final JsonArray vnetJsonArray = result.get("devices").asArray();
479 + assertThat(vnetJsonArray, notNullValue());
480 + assertEquals("Virtual devices array is not the correct size.",
481 + vdevSet.size(), vnetJsonArray.size());
482 +
483 + vdevSet.forEach(vdev -> assertThat(vnetJsonArray, hasVdev(vdev)));
484 +
485 + verify(mockVnetService);
486 + }
487 +
488 + /**
489 + * Array matcher for VirtualDevice.
490 + */
491 + public static class VdevJsonArrayMatcher extends JsonArrayMatcher<VirtualDevice> {
492 +
493 + public VdevJsonArrayMatcher(VirtualDevice vdevIn) {
494 + super(vdevIn,
495 + vdev -> "Virtual device " + vdev.networkId().toString()
496 + + " " + vdev.id().toString(),
497 + (vdev, jsonObject) -> {
498 + return jsonObject.get(ID).asString().equals(vdev.networkId().toString())
499 + && jsonObject.get(DEVICE_ID).asString().equals(vdev.id().toString()); },
500 + ImmutableList.of(ID, DEVICE_ID),
501 + (vdev, s) -> {
502 + return s.equals(ID) ? vdev.networkId().toString()
503 + : s.equals(DEVICE_ID) ? vdev.id().toString()
504 + : null;
505 + }
506 + );
507 + }
508 + }
509 +
510 + /**
511 + * Factory to allocate a virtual device array matcher.
512 + *
513 + * @param vdev virtual device object we are looking for
514 + * @return matcher
515 + */
516 + private VdevJsonArrayMatcher hasVdev(VirtualDevice vdev) {
517 + return new VdevJsonArrayMatcher(vdev);
518 + }
519 + /**
520 + * Tests adding of new virtual device using POST via JSON stream.
521 + */
522 + @Test
523 + public void testPostVirtualDevice() {
524 + NetworkId networkId = networkId3;
525 + DeviceId deviceId = devId2;
526 + expect(mockVnetAdminService.createVirtualDevice(networkId, deviceId)).andReturn(vdev2);
527 + expectLastCall();
528 +
529 + replay(mockVnetAdminService);
530 +
531 + WebTarget wt = target();
532 + InputStream jsonStream = VirtualNetworkWebResourceTest.class
533 + .getResourceAsStream("post-virtual-device.json");
534 + String reqLocation = "vnets/" + networkId.toString() + "/devices";
535 + Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
536 + .post(Entity.json(jsonStream));
537 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
538 +
539 + String location = response.getLocation().getPath();
540 + assertThat(location, Matchers.startsWith("/" + reqLocation + "/" + vdev2.id().toString()));
541 +
542 + verify(mockVnetAdminService);
543 + }
544 +
545 + /**
546 + * Tests adding of a null virtual device using POST via JSON stream.
547 + */
548 + @Test
549 + public void testPostVirtualDeviceNullJsonStream() {
550 + NetworkId networkId = networkId3;
551 + replay(mockVnetAdminService);
552 +
553 + WebTarget wt = target();
554 + try {
555 + String reqLocation = "vnets/" + networkId.toString() + "/devices";
556 + String response = wt.path(reqLocation)
557 + .request(MediaType.APPLICATION_JSON_TYPE)
558 + .post(Entity.json(null), String.class);
559 + fail("POST of null virtual device did not throw an exception");
560 + } catch (BadRequestException ex) {
561 + assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
562 + }
563 +
564 + verify(mockVnetAdminService);
565 + }
566 +
567 + /**
568 + * Tests removing a virtual device with DELETE request.
569 + */
570 + @Test
571 + public void testDeleteVirtualDevice() {
572 + NetworkId networkId = networkId3;
573 + DeviceId deviceId = devId2;
574 + mockVnetAdminService.removeVirtualDevice(networkId, deviceId);
575 + expectLastCall();
576 + replay(mockVnetAdminService);
577 +
578 + WebTarget wt = target()
579 + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
580 + String reqLocation = "vnets/" + networkId.toString() + "/devices/" + deviceId.toString();
581 + Response response = wt.path(reqLocation)
582 + .request(MediaType.APPLICATION_JSON_TYPE)
583 + .delete();
584 +
585 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
586 +
587 + verify(mockVnetAdminService);
588 + }
589 +
590 + // Tests for Virtual Ports
591 +
592 + /**
593 + * Tests the result of the REST API GET when there are no virtual ports.
594 + */
595 + @Test
596 + public void testGetVirtualPortsEmptyArray() {
597 + NetworkId networkId = networkId4;
598 + DeviceId deviceId = devId2;
599 + expect(mockVnetService.getVirtualPorts(networkId, deviceId))
600 + .andReturn(ImmutableSet.of()).anyTimes();
601 + replay(mockVnetService);
602 +
603 + WebTarget wt = target();
604 + String location = "vnets/" + networkId.toString()
605 + + "/devices/" + deviceId.toString() + "/ports";
606 + String response = wt.path(location).request().get(String.class);
607 + assertThat(response, is("{\"ports\":[]}"));
608 +
609 + verify(mockVnetService);
610 + }
611 +
612 + /**
613 + * Tests the result of the REST API GET when virtual ports are defined.
614 + */
615 + @Test
616 + public void testGetVirtualPortsArray() {
617 + NetworkId networkId = networkId3;
618 + DeviceId deviceId = dev22.id();
619 + vportSet.add(vport23);
620 + vportSet.add(vport22);
621 + expect(mockVnetService.getVirtualPorts(networkId, deviceId)).andReturn(vportSet).anyTimes();
622 + replay(mockVnetService);
623 +
624 + WebTarget wt = target();
625 + String location = "vnets/" + networkId.toString()
626 + + "/devices/" + deviceId.toString() + "/ports";
627 + String response = wt.path(location).request().get(String.class);
628 + assertThat(response, containsString("{\"ports\":["));
629 +
630 + final JsonObject result = Json.parse(response).asObject();
631 + assertThat(result, notNullValue());
632 +
633 + assertThat(result.names(), hasSize(1));
634 + assertThat(result.names().get(0), is("ports"));
635 +
636 + final JsonArray vnetJsonArray = result.get("ports").asArray();
637 + assertThat(vnetJsonArray, notNullValue());
638 + assertEquals("Virtual ports array is not the correct size.",
639 + vportSet.size(), vnetJsonArray.size());
640 +
641 + vportSet.forEach(vport -> assertThat(vnetJsonArray, hasVport(vport)));
642 +
643 + verify(mockVnetService);
644 + }
645 +
646 + /**
647 + * Array matcher for VirtualPort.
648 + */
649 + public static class VportJsonArrayMatcher extends JsonArrayMatcher<VirtualPort> {
650 +
651 + public VportJsonArrayMatcher(VirtualPort vportIn) {
652 + super(vportIn,
653 + vport -> "Virtual port " + vport.networkId().toString() + " "
654 + + vport.element().id().toString() + " " + vport.number().toString(),
655 + (vport, jsonObject) -> {
656 + return jsonObject.get(ID).asString().equals(vport.networkId().toString())
657 + && jsonObject.get(PORT_NUM).asString().equals(vport.number().toString())
658 + && jsonObject.get(DEVICE_ID).asString().equals(vport.element().id().toString()); },
659 + ImmutableList.of(ID, DEVICE_ID, PORT_NUM, PHYS_DEVICE_ID, PHYS_PORT_NUM),
660 + (vport, s) -> {
661 + return s.equals(ID) ? vport.networkId().toString()
662 + : s.equals(DEVICE_ID) ? vport.element().id().toString()
663 + : s.equals(PORT_NUM) ? vport.number().toString()
664 + : s.equals(PHYS_DEVICE_ID) ? vport.realizedBy().element().id().toString()
665 + : s.equals(PHYS_PORT_NUM) ? vport.realizedBy().number().toString()
666 + : null;
667 + }
668 + );
669 + }
670 + }
671 +
672 + /**
673 + * Factory to allocate a virtual port array matcher.
674 + *
675 + * @param vport virtual port object we are looking for
676 + * @return matcher
677 + */
678 + private VportJsonArrayMatcher hasVport(VirtualPort vport) {
679 + return new VportJsonArrayMatcher(vport);
680 + }
681 +
682 + /**
683 + * Tests adding of new virtual port using POST via JSON stream.
684 + */
685 + @Test
686 + public void testPostVirtualPort() {
687 + NetworkId networkId = networkId3;
688 + DeviceId deviceId = devId22;
689 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
690 + Device physDevice = new DefaultDevice(null, DeviceId.deviceId("dev1"),
691 + null, null, null, null, null, null, annotations);
692 + Port port1 = new DefaultPort(physDevice, portNumber(1), true);
693 + expect(mockVnetAdminService.createVirtualPort(networkId, deviceId, portNumber(22), port1))
694 + .andReturn(vport22);
695 +
696 + replay(mockVnetAdminService);
697 +
698 + WebTarget wt = target();
699 + InputStream jsonStream = VirtualNetworkWebResourceTest.class
700 + .getResourceAsStream("post-virtual-port.json");
701 + String reqLocation = "vnets/" + networkId.toString()
702 + + "/devices/" + deviceId.toString() + "/ports";
703 + Response response = wt.path(reqLocation).request(MediaType.APPLICATION_JSON_TYPE)
704 + .post(Entity.json(jsonStream));
705 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
706 +
707 + verify(mockVnetAdminService);
708 + }
709 +
710 + /**
711 + * Tests adding of a null virtual port using POST via JSON stream.
712 + */
713 + @Test
714 + public void testPostVirtualPortNullJsonStream() {
715 + NetworkId networkId = networkId3;
716 + DeviceId deviceId = devId2;
717 + replay(mockVnetAdminService);
718 +
719 + WebTarget wt = target();
720 + try {
721 + String reqLocation = "vnets/" + networkId.toString()
722 + + "/devices/" + deviceId.toString() + "/ports";
723 + String response = wt.path(reqLocation)
724 + .request(MediaType.APPLICATION_JSON_TYPE)
725 + .post(Entity.json(null), String.class);
726 + fail("POST of null virtual port did not throw an exception");
727 + } catch (BadRequestException ex) {
728 + assertThat(ex.getMessage(), containsString("HTTP 400 Bad Request"));
729 + }
730 +
731 + verify(mockVnetAdminService);
732 + }
733 +
734 + /**
735 + * Tests removing a virtual port with DELETE request.
736 + */
737 + @Test
738 + public void testDeleteVirtualPort() {
739 + NetworkId networkId = networkId3;
740 + DeviceId deviceId = devId2;
741 + PortNumber portNum = portNumber(2);
742 + mockVnetAdminService.removeVirtualPort(networkId, deviceId, portNum);
743 + expectLastCall();
744 + replay(mockVnetAdminService);
745 +
746 + WebTarget wt = target()
747 + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
748 + String reqLocation = "vnets/" + networkId.toString()
749 + + "/devices/" + deviceId.toString() + "/ports/" + portNum.toLong();
750 + Response response = wt.path(reqLocation)
751 + .request(MediaType.APPLICATION_JSON_TYPE)
752 + .delete();
753 +
754 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
755 +
756 + verify(mockVnetAdminService);
757 + }
758 +
759 + // TODO Tests for Virtual Links
760 +}
1 +{
2 + "networkId": "3",
3 + "deviceId": "dev22",
4 + "portNum": "22",
5 + "physDeviceId": "dev1",
6 + "physPortNum": "1"
7 +}