/* * Copyright 2016-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.rest.resources; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.onosproject.incubator.net.virtual.NetworkId; import org.onosproject.incubator.net.virtual.TenantId; import org.onosproject.incubator.net.virtual.VirtualDevice; import org.onosproject.incubator.net.virtual.VirtualHost; import org.onosproject.incubator.net.virtual.VirtualLink; import org.onosproject.incubator.net.virtual.VirtualNetwork; import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService; import org.onosproject.incubator.net.virtual.VirtualNetworkService; import org.onosproject.incubator.net.virtual.VirtualPort; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.onosproject.rest.AbstractWebResource; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Query and Manage Virtual Network elements. */ @Path("vnets") public class VirtualNetworkWebResource extends AbstractWebResource { private static final String MISSING_FIELD = "Missing "; private static final String INVALID_FIELD = "Invalid "; private final VirtualNetworkAdminService vnetAdminService = get(VirtualNetworkAdminService.class); private final VirtualNetworkService vnetService = get(VirtualNetworkService.class); @Context private UriInfo uriInfo; // VirtualNetwork /** * Returns all virtual networks. * * @return 200 OK with set of virtual networks * @onos.rsModel VirtualNetworks */ @GET @Produces(MediaType.APPLICATION_JSON) public Response getVirtualNetworks() { Set<TenantId> tenantIds = vnetAdminService.getTenantIds(); List<VirtualNetwork> allVnets = tenantIds.stream() .map(tenantId -> vnetService.getVirtualNetworks(tenantId)) .flatMap(Collection::stream) .collect(Collectors.toList()); return ok(encodeArray(VirtualNetwork.class, "vnets", allVnets)).build(); } /** * Returns the virtual networks with the specified tenant identifier. * * @param tenantId tenant identifier * @return 200 OK with a virtual network, 404 not found * @onos.rsModel VirtualNetworks */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{tenantId}") public Response getVirtualNetworkById(@PathParam("tenantId") String tenantId) { final TenantId existingTid = TenantWebResource.getExistingTenantId(vnetAdminService, TenantId.tenantId(tenantId)); Set<VirtualNetwork> vnets = vnetService.getVirtualNetworks(existingTid); return ok(encodeArray(VirtualNetwork.class, "vnets", vnets)).build(); } /** * Creates a virtual network from the JSON input stream. * * @param stream tenant identifier JSON stream * @return status of the request - CREATED if the JSON is correct, * BAD_REQUEST if the JSON is invalid * @onos.rsModel TenantId */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createVirtualNetwork(InputStream stream) { try { final TenantId tid = TenantId.tenantId(getFromJsonStream(stream, "id").asText()); VirtualNetwork newVnet = vnetAdminService.createVirtualNetwork(tid); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() .path("vnets") .path(newVnet.id().toString()); return Response .created(locationBuilder.build()) .build(); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Removes the virtual network with the specified network identifier. * * @param networkId network identifier * @return 204 NO CONTENT */ @DELETE @Path("{networkId}") public Response removeVirtualNetwork(@PathParam("networkId") long networkId) { NetworkId nid = NetworkId.networkId(networkId); vnetAdminService.removeVirtualNetwork(nid); return Response.noContent().build(); } // VirtualDevice /** * Returns all virtual network devices in a virtual network. * * @param networkId network identifier * @return 200 OK with set of virtual devices, 404 not found * @onos.rsModel VirtualDevices */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{networkId}/devices") public Response getVirtualDevices(@PathParam("networkId") long networkId) { NetworkId nid = NetworkId.networkId(networkId); Set<VirtualDevice> vdevs = vnetService.getVirtualDevices(nid); return ok(encodeArray(VirtualDevice.class, "devices", vdevs)).build(); } /** * Creates a virtual device from the JSON input stream. * * @param networkId network identifier * @param stream virtual device JSON stream * @return status of the request - CREATED if the JSON is correct, * BAD_REQUEST if the JSON is invalid * @onos.rsModel VirtualDevice */ @POST @Path("{networkId}/devices") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createVirtualDevice(@PathParam("networkId") long networkId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); final VirtualDevice vdevReq = codec(VirtualDevice.class).decode(jsonTree, this); JsonNode specifiedNetworkId = jsonTree.get("networkId"); if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } final VirtualDevice vdevRes = vnetAdminService.createVirtualDevice(vdevReq.networkId(), vdevReq.id()); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() .path("vnets").path(specifiedNetworkId.asText()) .path("devices").path(vdevRes.id().toString()); return Response .created(locationBuilder.build()) .build(); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Removes the virtual network device from the virtual network. * * @param networkId network identifier * @param deviceId device identifier * @return 204 NO CONTENT */ @DELETE @Path("{networkId}/devices/{deviceId}") public Response removeVirtualDevice(@PathParam("networkId") long networkId, @PathParam("deviceId") String deviceId) { NetworkId nid = NetworkId.networkId(networkId); DeviceId did = DeviceId.deviceId(deviceId); vnetAdminService.removeVirtualDevice(nid, did); return Response.noContent().build(); } // VirtualPort /** * Returns all virtual network ports in a virtual device in a virtual network. * * @param networkId network identifier * @param deviceId virtual device identifier * @return 200 OK with set of virtual ports, 404 not found * @onos.rsModel VirtualPorts */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{networkId}/devices/{deviceId}/ports") public Response getVirtualPorts(@PathParam("networkId") long networkId, @PathParam("deviceId") String deviceId) { NetworkId nid = NetworkId.networkId(networkId); Iterable<VirtualPort> vports = vnetService.getVirtualPorts(nid, DeviceId.deviceId(deviceId)); return ok(encodeArray(VirtualPort.class, "ports", vports)).build(); } /** * Creates a virtual network port in a virtual device in a virtual network. * * @param networkId network identifier * @param virtDeviceId virtual device identifier * @param stream virtual port JSON stream * @return status of the request - CREATED if the JSON is correct, * BAD_REQUEST if the JSON is invalid * @onos.rsModel VirtualPort */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("{networkId}/devices/{deviceId}/ports") public Response createVirtualPort(@PathParam("networkId") long networkId, @PathParam("deviceId") String virtDeviceId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); // final VirtualPort vportReq = codec(VirtualPort.class).decode(jsonTree, this); JsonNode specifiedNetworkId = jsonTree.get("networkId"); JsonNode specifiedDeviceId = jsonTree.get("deviceId"); if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } if (specifiedDeviceId == null || !specifiedDeviceId.asText().equals(virtDeviceId)) { throw new IllegalArgumentException(INVALID_FIELD + "deviceId"); } JsonNode specifiedPortNum = jsonTree.get("portNum"); JsonNode specifiedPhysDeviceId = jsonTree.get("physDeviceId"); JsonNode specifiedPhysPortNum = jsonTree.get("physPortNum"); final NetworkId nid = NetworkId.networkId(networkId); DeviceId vdevId = DeviceId.deviceId(virtDeviceId); ConnectPoint realizedBy = new ConnectPoint(DeviceId.deviceId(specifiedPhysDeviceId.asText()), PortNumber.portNumber(specifiedPhysPortNum.asText())); VirtualPort vport = vnetAdminService.createVirtualPort(nid, vdevId, PortNumber.portNumber(specifiedPortNum.asText()), realizedBy); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() .path("vnets").path(specifiedNetworkId.asText()) .path("devices").path(specifiedDeviceId.asText()) .path("ports").path(vport.number().toString()); return Response .created(locationBuilder.build()) .build(); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Removes the virtual network port from the virtual device in a virtual network. * * @param networkId network identifier * @param deviceId virtual device identifier * @param portNum virtual port number * @return 204 NO CONTENT */ @DELETE @Path("{networkId}/devices/{deviceId}/ports/{portNum}") public Response removeVirtualPort(@PathParam("networkId") long networkId, @PathParam("deviceId") String deviceId, @PathParam("portNum") long portNum) { NetworkId nid = NetworkId.networkId(networkId); vnetAdminService.removeVirtualPort(nid, DeviceId.deviceId(deviceId), PortNumber.portNumber(portNum)); return Response.noContent().build(); } // VirtualLink /** * Returns all virtual network links in a virtual network. * * @param networkId network identifier * @return 200 OK with set of virtual network links * @onos.rsModel VirtualLinks */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{networkId}/links") public Response getVirtualLinks(@PathParam("networkId") long networkId) { NetworkId nid = NetworkId.networkId(networkId); Set<VirtualLink> vlinks = vnetService.getVirtualLinks(nid); return ok(encodeArray(VirtualLink.class, "links", vlinks)).build(); } /** * Creates a virtual network link from the JSON input stream. * * @param networkId network identifier * @param stream virtual link JSON stream * @return status of the request - CREATED if the JSON is correct, * BAD_REQUEST if the JSON is invalid * @onos.rsModel VirtualLink */ @POST @Path("{networkId}/links") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createVirtualLink(@PathParam("networkId") long networkId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); JsonNode specifiedNetworkId = jsonTree.get("networkId"); if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } final VirtualLink vlinkReq = codec(VirtualLink.class).decode(jsonTree, this); vnetAdminService.createVirtualLink(vlinkReq.networkId(), vlinkReq.src(), vlinkReq.dst()); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() .path("vnets").path(specifiedNetworkId.asText()) .path("links"); return Response .created(locationBuilder.build()) .build(); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Removes the virtual network link from the JSON input stream. * * @param networkId network identifier * @param stream virtual link JSON stream * @return 204 NO CONTENT * @onos.rsModel VirtualLink */ @DELETE @Path("{networkId}/links") @Consumes(MediaType.APPLICATION_JSON) public Response removeVirtualLink(@PathParam("networkId") long networkId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); JsonNode specifiedNetworkId = jsonTree.get("networkId"); if (specifiedNetworkId != null && specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } final VirtualLink vlinkReq = codec(VirtualLink.class).decode(jsonTree, this); vnetAdminService.removeVirtualLink(vlinkReq.networkId(), vlinkReq.src(), vlinkReq.dst()); } catch (IOException e) { throw new IllegalArgumentException(e); } return Response.noContent().build(); } /** * Returns all virtual network hosts in a virtual network. * * @param networkId network identifier * @return 200 OK with set of virtual network hosts * @onos.rsModel VirtualHosts */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{networkId}/hosts") public Response getVirtualHosts(@PathParam("networkId") long networkId) { NetworkId nid = NetworkId.networkId(networkId); Set<VirtualHost> vhosts = vnetService.getVirtualHosts(nid); return ok(encodeArray(VirtualHost.class, "hosts", vhosts)).build(); } /** * Creates a virtual network host from the JSON input stream. * * @param networkId network identifier * @param stream virtual host JSON stream * @return status of the request - CREATED if the JSON is correct, * BAD_REQUEST if the JSON is invalid * @onos.rsModel VirtualHostPut */ @POST @Path("{networkId}/hosts") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response createVirtualHost(@PathParam("networkId") long networkId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); JsonNode specifiedNetworkId = jsonTree.get("networkId"); if (specifiedNetworkId == null || specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } final VirtualHost vhostReq = codec(VirtualHost.class).decode(jsonTree, this); vnetAdminService.createVirtualHost(vhostReq.networkId(), vhostReq.id(), vhostReq.mac(), vhostReq.vlan(), vhostReq.location(), vhostReq.ipAddresses()); UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() .path("vnets").path(specifiedNetworkId.asText()) .path("hosts"); return Response .created(locationBuilder.build()) .build(); } catch (IOException e) { throw new IllegalArgumentException(e); } } /** * Removes the virtual network host from the JSON input stream. * * @param networkId network identifier * @param stream virtual host JSON stream * @return 204 NO CONTENT * @onos.rsModel VirtualHost */ @DELETE @Path("{networkId}/hosts") @Consumes(MediaType.APPLICATION_JSON) public Response removeVirtualHost(@PathParam("networkId") long networkId, InputStream stream) { try { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); JsonNode specifiedNetworkId = jsonTree.get("networkId"); if (specifiedNetworkId != null && specifiedNetworkId.asLong() != (networkId)) { throw new IllegalArgumentException(INVALID_FIELD + "networkId"); } final VirtualHost vhostReq = codec(VirtualHost.class).decode(jsonTree, this); vnetAdminService.removeVirtualHost(vhostReq.networkId(), vhostReq.id()); } catch (IOException e) { throw new IllegalArgumentException(e); } return Response.noContent().build(); } /** * Get the tenant identifier from the JSON stream. * * @param stream TenantId JSON stream * @param jsonFieldName field name * @return JsonNode * @throws IOException if unable to parse the request */ private JsonNode getFromJsonStream(InputStream stream, String jsonFieldName) throws IOException { ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); JsonNode jsonNode = jsonTree.get(jsonFieldName); if (jsonNode == null) { throw new IllegalArgumentException(MISSING_FIELD + jsonFieldName); } return jsonNode; } }