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 + "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 + "networkId": "3",
3 + "deviceId": "dev22",
4 + "portNum": "22",
5 + "physDeviceId": "dev1",
6 + "physPortNum": "1"
7 +}