Jian Li
Committed by Ray Milkey

[ONOS-4438] Add REST API for mastership service

- Correct some typos in mastership codec

Change-Id: If8a0127d8d897d4b87cae71a194dfece6aa14f49
...@@ -39,7 +39,7 @@ public final class RoleInfoCodec extends JsonCodec<RoleInfo> { ...@@ -39,7 +39,7 @@ public final class RoleInfoCodec extends JsonCodec<RoleInfo> {
39 private static final String MASTER = "master"; 39 private static final String MASTER = "master";
40 private static final String BACKUPS = "backups"; 40 private static final String BACKUPS = "backups";
41 41
42 - private static final String MISSING_MEMBER_MESSAGE = " member is required in MastershipTerm"; 42 + private static final String MISSING_MEMBER_MESSAGE = " member is required in RoleInfo";
43 43
44 @Override 44 @Override
45 public ObjectNode encode(RoleInfo roleInfo, CodecContext context) { 45 public ObjectNode encode(RoleInfo roleInfo, CodecContext context) {
......
...@@ -49,7 +49,8 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -49,7 +49,8 @@ public class CoreWebApplication extends AbstractWebApplication {
49 DeviceKeyWebResource.class, 49 DeviceKeyWebResource.class,
50 RegionsWebResource.class, 50 RegionsWebResource.class,
51 TenantWebResource.class, 51 TenantWebResource.class,
52 - VirtualNetworkWebResource.class 52 + VirtualNetworkWebResource.class,
53 + MastershipWebResource.class
53 ); 54 );
54 } 55 }
55 } 56 }
......
1 +/*
2 + * Copyright 2016-present 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.rest.resources;
17 +
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import org.onosproject.cluster.NodeId;
22 +import org.onosproject.cluster.RoleInfo;
23 +import org.onosproject.mastership.MastershipAdminService;
24 +import org.onosproject.mastership.MastershipService;
25 +import org.onosproject.net.DeviceId;
26 +import org.onosproject.net.MastershipRole;
27 +import org.onosproject.rest.AbstractWebResource;
28 +
29 +import javax.ws.rs.Consumes;
30 +import javax.ws.rs.GET;
31 +import javax.ws.rs.PUT;
32 +import javax.ws.rs.Path;
33 +import javax.ws.rs.PathParam;
34 +import javax.ws.rs.Produces;
35 +import javax.ws.rs.core.MediaType;
36 +import javax.ws.rs.core.Response;
37 +import java.io.IOException;
38 +import java.io.InputStream;
39 +import java.util.Set;
40 +import java.util.concurrent.CompletableFuture;
41 +import java.util.concurrent.ExecutionException;
42 +
43 +import static org.onlab.util.Tools.nullIsNotFound;
44 +
45 +/**
46 + * Manage the mastership of ONOS instances.
47 + */
48 +@Path("mastership")
49 +public final class MastershipWebResource extends AbstractWebResource {
50 +
51 + private static final String NODE = "node";
52 + private static final String DEVICES = "devices";
53 + private static final String DEVICE_ID = "deviceId";
54 + private static final String NODE_ID = "nodeId";
55 +
56 + private static final String DEVICE_ID_INVALID = "Invalid deviceId for setting role";
57 + private static final String NODE_ID_INVALID = "Invalid nodeId for setting role";
58 +
59 + private static final String NODE_ID_NOT_FOUND = "Node id is not found";
60 + private static final String ROLE_INFO_NOT_FOUND = "Role info is not found";
61 + private static final String MASTERSHIP_ROLE_NOT_FOUND = "Mastership role is not found";
62 + private static final String RESULT_NOT_FOUND = "Result is not found";
63 +
64 + private final MastershipService mastershipService = get(MastershipService.class);
65 + private final MastershipAdminService mastershipAdminService =
66 + get(MastershipAdminService.class);
67 +
68 + /**
69 + * Returns the role of the local node for the specified device.
70 + *
71 + * @param deviceId device identifier
72 + * @return role of the current node
73 + * @onos.rsModel MastershipRole
74 + */
75 + @GET
76 + @Produces(MediaType.APPLICATION_JSON)
77 + @Path("{deviceId}/local")
78 + public Response getLocalRole(@PathParam("deviceId") String deviceId) {
79 + MastershipRole role = mastershipService.getLocalRole(DeviceId.deviceId(deviceId));
80 + ObjectNode root = codec(MastershipRole.class).encode(role, this);
81 + return ok(root).build();
82 + }
83 +
84 + /**
85 + * Returns the current master for a given device.
86 + *
87 + * @param deviceId device identifier
88 + * @return the identifier of the master controller for the device
89 + * // TODO: add swagger doc
90 + */
91 + @GET
92 + @Produces(MediaType.APPLICATION_JSON)
93 + @Path("{deviceId}/master")
94 + public Response getMasterFor(@PathParam("deviceId") String deviceId) {
95 + NodeId id = nullIsNotFound(mastershipService.getMasterFor(
96 + DeviceId.deviceId(deviceId)), NODE_ID_NOT_FOUND);
97 +
98 + ObjectNode root = mapper().createObjectNode();
99 + root.put(NODE, id.id());
100 + return ok(root).build();
101 + }
102 +
103 + /**
104 + * Returns controllers connected to a given device, in order of
105 + * preference. The first entry in the list is the current master.
106 + *
107 + * @param deviceId device identifier
108 + * @return a list of controller identifiers
109 + * @onos.rsModel RoleInfo
110 + */
111 + @GET
112 + @Produces(MediaType.APPLICATION_JSON)
113 + @Path("{deviceId}/role")
114 + public Response getNodesFor(@PathParam("deviceId") String deviceId) {
115 + RoleInfo info = nullIsNotFound(mastershipService.getNodesFor(
116 + DeviceId.deviceId(deviceId)), ROLE_INFO_NOT_FOUND);
117 + ObjectNode root = codec(RoleInfo.class).encode(info, this);
118 + return ok(root).build();
119 + }
120 +
121 + /**
122 + * Returns the devices for which a controller is master.
123 + *
124 + * @param nodeId controller identifier
125 + * @return a set of device identifiers
126 + * // TODO: add swagger doc
127 + */
128 + @GET
129 + @Produces(MediaType.APPLICATION_JSON)
130 + @Path("{nodeId}/device")
131 + public Response getDeviceOf(@PathParam("nodeId") String nodeId) {
132 + ObjectNode root = mapper().createObjectNode();
133 + ArrayNode devicesNode = root.putArray(DEVICES);
134 +
135 + Set<DeviceId> devices = mastershipService.getDevicesOf(NodeId.nodeId(nodeId));
136 + if (devices != null) {
137 + devices.forEach(id -> devicesNode.add(id.toString()));
138 + }
139 +
140 + return ok(root).build();
141 + }
142 +
143 + /**
144 + * Returns the mastership status of the local controller for a given
145 + * device forcing master selection if necessary.
146 + *
147 + * @param deviceId device identifier
148 + * @return the role of this controller instance
149 + * @onos.rsModel MastershipRole
150 + */
151 + @GET
152 + @Produces(MediaType.APPLICATION_JSON)
153 + @Path("{deviceId}/request")
154 + public Response requestRoleFor(@PathParam("deviceId") String deviceId) {
155 +
156 + // TODO: will not use CompletableFuture when MastershipService
157 + // provides a non CompletableFuture object as an output
158 + CompletableFuture<MastershipRole> result =
159 + nullIsNotFound(mastershipService.requestRoleFor(
160 + DeviceId.deviceId(deviceId)), MASTERSHIP_ROLE_NOT_FOUND);
161 +
162 + try {
163 + MastershipRole role = result.get();
164 + ObjectNode root = codec(MastershipRole.class).encode(role, this);
165 + return ok(root).build();
166 + } catch (InterruptedException | ExecutionException e) {
167 + throw new IllegalArgumentException(e);
168 + }
169 + }
170 +
171 + /**
172 + * Abandons mastership of the specified device on the local node thus
173 + * forcing selection of a new master. If the local node is not a master
174 + * for this device, no master selection will occur.
175 + *
176 + * @param deviceId device identifier
177 + * @return status of the request
178 + */
179 + @GET
180 + @Produces(MediaType.APPLICATION_JSON)
181 + @Path("{deviceId}/relinquish")
182 + public Response relinquishMastership(@PathParam("deviceId") String deviceId) {
183 + DeviceId id = DeviceId.deviceId(deviceId);
184 +
185 + // TODO: will not use CompletableFuture when MastershipService
186 + // provides a non CompletableFuture object as an output
187 + CompletableFuture<Void> result =
188 + nullIsNotFound(mastershipService.relinquishMastership(id), RESULT_NOT_FOUND);
189 +
190 + try {
191 + result.get();
192 + return Response.created(id.uri()).build();
193 + } catch (InterruptedException | ExecutionException e) {
194 + throw new IllegalArgumentException(e);
195 + }
196 + }
197 +
198 + /**
199 + * Applies the current mastership role for the specified device.
200 + *
201 + * @param stream JSON representation of device, node, mastership info
202 + * @return status of the request - CREATED if the JSON is correct,
203 + * BAD_REQUEST if the JSON is invalid
204 + * @onos.rsModel MastershipPut
205 + */
206 + @PUT
207 + @Consumes(MediaType.APPLICATION_JSON)
208 + @Produces(MediaType.APPLICATION_JSON)
209 + public Response setRole(InputStream stream) {
210 +
211 + try {
212 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
213 + JsonNode deviceIdJson = jsonTree.get(DEVICE_ID);
214 + JsonNode nodeIdJson = jsonTree.get(NODE_ID);
215 + MastershipRole role = codec(MastershipRole.class).decode(jsonTree, this);
216 +
217 + if (deviceIdJson == null) {
218 + throw new IllegalArgumentException(DEVICE_ID_INVALID);
219 + }
220 +
221 + if (nodeIdJson == null) {
222 + throw new IllegalArgumentException(NODE_ID_INVALID);
223 + }
224 +
225 + // TODO: will not use CompletableFuture when MastershipAdminService
226 + // provides a non CompletableFuture object as an output
227 + CompletableFuture<Void> result =
228 + nullIsNotFound(mastershipAdminService.setRole(NodeId.nodeId(nodeIdJson.asText()),
229 + DeviceId.deviceId(deviceIdJson.asText()), role), RESULT_NOT_FOUND);
230 + result.get();
231 +
232 + return Response.ok().build();
233 + } catch (InterruptedException | ExecutionException | IOException e) {
234 + throw new IllegalArgumentException(e);
235 + }
236 + }
237 +
238 + /**
239 + * Balances the mastership to be shared as evenly as possibly by all
240 + * online instances.
241 + *
242 + * @return status of the request - OK if the request is successfully processed
243 + */
244 + @GET
245 + @Produces(MediaType.APPLICATION_JSON)
246 + public Response balanceRoles() {
247 + mastershipAdminService.balanceRoles();
248 + return Response.ok().build();
249 + }
250 +}
1 +{
2 + "type": "object",
3 + "title": "mastership",
4 + "required": [
5 + "deviceId",
6 + "nodeId",
7 + "mastershipRole"
8 + ],
9 + "properties": {
10 + "deviceId": {
11 + "type": "string",
12 + "example": "of:0000000000000001"
13 + },
14 + "nodeId": {
15 + "type": "string",
16 + "example": "1"
17 + },
18 + "mastershipRole": {
19 + "type": "string",
20 + "example": "MASTER"
21 + }
22 + }
23 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "type": "object",
3 + "title": "mastershipRole",
4 + "required": [
5 + "role"
6 + ],
7 + "properties": {
8 + "role": {
9 + "type": "string",
10 + "example": "MASTER"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "type": "object",
3 + "title": "roleInfo",
4 + "required": [
5 + "master",
6 + "backups"
7 + ],
8 + "properties": {
9 + "master": {
10 + "type": "string",
11 + "example": "1"
12 + },
13 + "backups": {
14 + "type": "array",
15 + "xml": {
16 + "name": "backups",
17 + "wrapped": true
18 + },
19 + "items": {
20 + "type": "string",
21 + "example": "1"
22 + }
23 + }
24 + }
25 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "deviceId": "of:0000000000000001",
3 + "nodeId": "1",
4 + "role": "MASTER"
5 +}
...\ No newline at end of file ...\ No newline at end of file