Claudine Chiu
Committed by Gerrit Code Review

ONOS-2184 VirtualHost CLI and REST api's

Change-Id: If0ebe4268f3161a34223eca58e3f1bdbb8d0c9be
/*
* 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.cli.net.vnet;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Creates a new virtual host.
*/
@Command(scope = "onos", name = "vnet-create-host",
description = "Creates a new virtual host in a network.")
public class VirtualHostCreateCommand extends AbstractShellCommand {
@Argument(index = 0, name = "networkId", description = "Network ID",
required = true, multiValued = false)
Long networkId = null;
@Argument(index = 1, name = "mac", description = "Mac address",
required = true, multiValued = false)
String mac = null;
@Argument(index = 2, name = "vlan", description = "Vlan",
required = true, multiValued = false)
short vlan;
@Argument(index = 3, name = "hostLocationDeviceId", description = "Host location device ID",
required = true, multiValued = false)
String hostLocationDeviceId;
@Argument(index = 4, name = "hostLocationPortNumber", description = "Host location port number",
required = true, multiValued = false)
long hostLocationPortNumber;
// ip addresses
@Option(name = "--hostIp", description = "Host IP addresses. Can be specified multiple times.",
required = false, multiValued = true)
protected String[] hostIpStrings;
@Override
protected void execute() {
VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
Set<IpAddress> hostIps = new HashSet<>();
if (hostIpStrings != null) {
Arrays.asList(hostIpStrings).stream().forEach(s -> hostIps.add(IpAddress.valueOf(s)));
}
HostLocation hostLocation = new HostLocation(DeviceId.deviceId(hostLocationDeviceId),
PortNumber.portNumber(hostLocationPortNumber),
System.currentTimeMillis());
MacAddress macAddress = MacAddress.valueOf(mac);
VlanId vlanId = VlanId.vlanId(vlan);
service.createVirtualHost(NetworkId.networkId(networkId),
HostId.hostId(macAddress, vlanId), macAddress, vlanId,
hostLocation, hostIps);
print("Virtual host successfully created.");
}
}
/*
* 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.cli.net.vnet;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualHost;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import java.util.ArrayList;
import java.util.List;
/**
* Lists all virtual hosts for the network ID.
*/
@Command(scope = "onos", name = "vnet-hosts",
description = "Lists all virtual hosts in a virtual network.")
public class VirtualHostListCommand extends AbstractShellCommand {
private static final String FMT_VIRTUAL_HOST =
"id=%s, mac=%s, vlan=%s, location=%s, ips=%s";
@Argument(index = 0, name = "networkId", description = "Network ID",
required = true, multiValued = false)
Long networkId = null;
@Override
protected void execute() {
getSortedVirtualHosts().forEach(this::printVirtualHost);
}
/**
* Returns the list of virtual hosts sorted using the device identifier.
*
* @return virtual host list
*/
private List<VirtualHost> getSortedVirtualHosts() {
VirtualNetworkService service = get(VirtualNetworkService.class);
List<VirtualHost> virtualHosts = new ArrayList<>();
virtualHosts.addAll(service.getVirtualHosts(NetworkId.networkId(networkId)));
return virtualHosts;
}
/**
* Prints out each virtual host.
*
* @param virtualHost virtual host
*/
private void printVirtualHost(VirtualHost virtualHost) {
print(FMT_VIRTUAL_HOST, virtualHost.id().toString(), virtualHost.mac().toString(),
virtualHost.vlan().toString(), virtualHost.location().toString(),
virtualHost.ipAddresses().toString());
}
}
/*
* 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.cli.net.vnet;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
import org.onosproject.net.HostId;
//import org.onosproject.net.HostId;
/**
* Removes a virtual host.
*/
@Command(scope = "onos", name = "vnet-remove-host",
description = "Removes a virtual host.")
public class VirtualHostRemoveCommand extends AbstractShellCommand {
@Argument(index = 0, name = "networkId", description = "Network ID",
required = true, multiValued = false)
Long networkId = null;
@Argument(index = 1, name = "id", description = "Host ID",
required = true, multiValued = false)
String id = null;
@Override
protected void execute() {
VirtualNetworkAdminService service = get(VirtualNetworkAdminService.class);
service.removeVirtualHost(NetworkId.networkId(networkId), HostId.hostId(id));
print("Virtual host successfully removed.");
}
}
......@@ -674,6 +674,15 @@
<command>
<action class="org.onosproject.cli.net.vnet.VirtualPortRemoveCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.vnet.VirtualHostListCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.vnet.VirtualHostCreateCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.vnet.VirtualHostRemoveCommand"/>
</command>
</command-bundle>
<bean id="reviewAppNameCompleter" class="org.onosproject.cli.security.ReviewApplicationNameCompleter"/>
......
......@@ -30,6 +30,7 @@ import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
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.VirtualPort;
......@@ -141,6 +142,7 @@ public class CodecManager implements CodecService {
registerCodec(VirtualDevice.class, new VirtualDeviceCodec());
registerCodec(VirtualPort.class, new VirtualPortCodec());
registerCodec(VirtualLink.class, new VirtualLinkCodec());
registerCodec(VirtualHost.class, new VirtualHostCodec());
registerCodec(MastershipTerm.class, new MastershipTermCodec());
registerCodec(MastershipRole.class, new MastershipRoleCodec());
registerCodec(RoleInfo.class, new RoleInfoCodec());
......
/*
* 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.codec.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.incubator.net.virtual.DefaultVirtualHost;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.PortNumber;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.nullIsIllegal;
/**
* Codec for the VirtualHost class.
*/
public class VirtualHostCodec extends JsonCodec<VirtualHost> {
// JSON field names
private static final String NETWORK_ID = "networkId";
private static final String HOST_ID = "id";
private static final String MAC_ADDRESS = "mac";
private static final String VLAN = "vlan";
private static final String IP_ADDRESSES = "ipAddresses";
private static final String HOST_LOCATION = "location";
private static final String NULL_OBJECT_MSG = "VirtualHost cannot be null";
private static final String MISSING_MEMBER_MSG = " member is required in VirtualHost";
@Override
public ObjectNode encode(VirtualHost vHost, CodecContext context) {
checkNotNull(vHost, NULL_OBJECT_MSG);
final JsonCodec<HostLocation> locationCodec =
context.codec(HostLocation.class);
final ObjectNode result = context.mapper().createObjectNode()
.put(NETWORK_ID, vHost.networkId().toString())
.put(HOST_ID, vHost.id().toString())
.put(MAC_ADDRESS, vHost.mac().toString())
.put(VLAN, vHost.vlan().toString());
final ArrayNode jsonIpAddresses = result.putArray(IP_ADDRESSES);
for (final IpAddress ipAddress : vHost.ipAddresses()) {
jsonIpAddresses.add(ipAddress.toString());
}
result.set(IP_ADDRESSES, jsonIpAddresses);
result.set(HOST_LOCATION, locationCodec.encode(vHost.location(), context));
return result;
}
@Override
public VirtualHost decode(ObjectNode json, CodecContext context) {
if (json == null || !json.isObject()) {
return null;
}
NetworkId nId = NetworkId.networkId(Long.parseLong(extractMember(NETWORK_ID, json)));
MacAddress mac = MacAddress.valueOf(json.get("mac").asText());
VlanId vlanId = VlanId.vlanId((short) json.get("vlan").asInt(VlanId.UNTAGGED));
JsonNode locationNode = json.get("location");
PortNumber portNumber = PortNumber.portNumber(locationNode.get("port").asText());
DeviceId deviceId = DeviceId.deviceId(locationNode.get("elementId").asText());
HostLocation hostLocation = new HostLocation(deviceId, portNumber, 0);
HostId id = HostId.hostId(mac, vlanId);
Iterator<JsonNode> ipStrings = json.get("ipAddresses").elements();
Set<IpAddress> ips = new HashSet<>();
while (ipStrings.hasNext()) {
ips.add(IpAddress.valueOf(ipStrings.next().asText()));
}
return new DefaultVirtualHost(nId, id, mac, vlanId, hostLocation, ips);
}
/**
* Extract member from JSON ObjectNode.
*
* @param key key for which value is needed
* @param json JSON ObjectNode
* @return member value
*/
private String extractMember(String key, ObjectNode json) {
return nullIsIllegal(json.get(key), key + MISSING_MEMBER_MSG).asText();
}
}
......@@ -21,6 +21,7 @@ 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;
......@@ -390,6 +391,87 @@ public class VirtualNetworkWebResource extends AbstractWebResource {
}
/**
* 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
......
{
"type": "object",
"title": "host",
"required": [
"networkId",
"id",
"mac",
"vlan",
"ipAddresses",
"location"
],
"properties": {
"networkId": {
"type": "int64",
"description": "Network identifier",
"example": 3
},
"id": {
"type": "string",
"example": "46:E4:3C:A4:17:C8/-1"
},
"mac": {
"type": "string",
"example": "46:E4:3C:A4:17:C8"
},
"vlan": {
"type": "string",
"example": "-1"
},
"ipAddresses": {
"type": "array",
"xml": {
"name": "hosts",
"wrapped": true
},
"items": {
"type": "string",
"example": "127.0.0.1"
}
},
"location": {
"type": "object",
"title": "location",
"required": [
"elementId",
"port"
],
"properties": {
"elementId": {
"type": "string",
"example": "of:0000000000000002"
},
"port": {
"type": "string",
"example": "3"
}
}
}
}
}
{
"type": "object",
"title": "host",
"required": [
"networkId",
"mac",
"vlan",
"ipAddresses",
"location"
],
"properties": {
"networkId": {
"type": "int64",
"description": "Network identifier",
"example": 3
},
"mac": {
"type": "string",
"example": "46:E4:3C:A4:17:C8"
},
"vlan": {
"type": "string",
"example": "-1"
},
"ipAddresses": {
"type": "array",
"xml": {
"name": "hosts",
"wrapped": true
},
"items": {
"type": "string",
"example": "127.0.0.1"
}
},
"location": {
"type": "object",
"title": "location",
"required": [
"elementId",
"port"
],
"properties": {
"elementId": {
"type": "string",
"example": "of:0000000000000002"
},
"port": {
"type": "string",
"example": "3"
}
}
}
}
}
{
"type": "object",
"title": "hosts",
"required": [
"hosts"
],
"properties": {
"hosts": {
"type": "array",
"xml": {
"name": "hosts",
"wrapped": true
},
"items": {
"type": "object",
"title": "host",
"required": [
"networkId",
"id",
"mac",
"vlan",
"ipAddresses",
"location"
],
"properties": {
"networkId": {
"type": "int64",
"description": "Network identifier",
"example": 3
},
"id": {
"type": "string",
"example": "46:E4:3C:A4:17:C8/-1"
},
"mac": {
"type": "string",
"example": "46:E4:3C:A4:17:C8"
},
"vlan": {
"type": "string",
"example": "-1"
},
"ipAddresses": {
"type": "array",
"xml": {
"name": "hosts",
"wrapped": true
},
"items": {
"type": "string",
"example": "127.0.0.1"
}
},
"location": {
"type": "object",
"title": "location",
"required": [
"elementId",
"port"
],
"properties": {
"elementId": {
"type": "string",
"example": "of:0000000000000002"
},
"port": {
"type": "string",
"example": "3"
}
}
}
}
}
}
}
}
{
"networkId": "3",
"id": "00:11:00:00:00:01/11",
"mac": "00:11:00:00:00:01",
"vlan": "11",
"location": {
"elementId": "devid1",
"port": "100"
},
"ipAddresses": [
"10.0.0.1",
"10.0.0.2"
]
}