blob: 4dfc28f02a215f83275a785a934c8ee1e4fcc609 [file] [log] [blame]
Jian Lif96d41f2016-05-03 09:49:12 -07001/*
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 */
16package org.onosproject.rest.resources;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import org.onosproject.cluster.NodeId;
22import org.onosproject.cluster.RoleInfo;
23import org.onosproject.mastership.MastershipAdminService;
24import org.onosproject.mastership.MastershipService;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.MastershipRole;
27import org.onosproject.rest.AbstractWebResource;
28
29import javax.ws.rs.Consumes;
30import javax.ws.rs.GET;
31import javax.ws.rs.PUT;
32import javax.ws.rs.Path;
33import javax.ws.rs.PathParam;
34import javax.ws.rs.Produces;
35import javax.ws.rs.core.MediaType;
36import javax.ws.rs.core.Response;
37import java.io.IOException;
38import java.io.InputStream;
39import java.util.Set;
40import java.util.concurrent.CompletableFuture;
41import java.util.concurrent.ExecutionException;
42
43import static org.onlab.util.Tools.nullIsNotFound;
44
45/**
46 * Manage the mastership of ONOS instances.
47 */
48@Path("mastership")
49public 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}