2 * Copyright (C) 2014 Red Hat, Inc. and others
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 * Authors : Madhu Venugopal, Brent Salisbury, Dave Tucker
10 package org.opendaylight.ovsdb.northbound;
12 import java.io.IOException;
14 import java.util.concurrent.ExecutionException;
16 import javax.ws.rs.Consumes;
17 import javax.ws.rs.DELETE;
18 import javax.ws.rs.GET;
19 import javax.ws.rs.POST;
20 import javax.ws.rs.PUT;
21 import javax.ws.rs.Path;
22 import javax.ws.rs.PathParam;
23 import javax.ws.rs.Produces;
24 import javax.ws.rs.core.Context;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
27 import javax.ws.rs.core.SecurityContext;
28 import javax.ws.rs.core.UriInfo;
30 import org.codehaus.enunciate.jaxrs.ResponseCode;
31 import org.codehaus.enunciate.jaxrs.StatusCodes;
32 import org.codehaus.enunciate.jaxrs.TypeHint;
33 import org.opendaylight.controller.northbound.commons.RestMessages;
34 import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
35 import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
36 import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
37 import org.opendaylight.controller.northbound.commons.exception.UnauthorizedException;
38 import org.opendaylight.controller.northbound.commons.utils.NorthboundUtils;
39 import org.opendaylight.controller.sal.authorization.Privilege;
40 import org.opendaylight.ovsdb.plugin.api.Status;
41 import org.opendaylight.ovsdb.lib.OvsdbClient;
42 import org.opendaylight.ovsdb.lib.notation.Row;
43 import org.opendaylight.ovsdb.lib.notation.UUID;
44 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
45 import org.opendaylight.ovsdb.plugin.api.OvsVswitchdSchemaConstants;
46 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
47 import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService;
48 import org.opendaylight.ovsdb.plugin.api.StatusWithUuid;
49 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 import com.fasterxml.jackson.databind.JsonNode;
57 * OVSDB Northbound REST API.<br>
58 * This class provides REST APIs to Create, Read, Update and Delete OVSDB Row in any of the ovsdb table
59 * database one at a time. The JSON used to create rows is in the same format as the OVSDB JSON-RPC messages.
60 * This format is documented in the <a href="http://openvswitch.org/ovs-vswitchd.conf.db.5.pdf">OVSDB Schema</a>
61 * and in <a href="http://tools.ietf.org/rfc/rfc7047.txt">RFC 7047</a>.
65 * Authentication scheme : <b>HTTP Basic</b><br>
66 * Authentication realm : <b>opendaylight</b><br>
67 * Transport : <b>HTTP and HTTPS</b><br>
69 * HTTPS Authentication is disabled by default.
74 public class OvsdbNorthboundV2 {
75 protected static final Logger logger = LoggerFactory.getLogger(OvsdbNorthboundV2.class);
78 private UriInfo _uriInfo;
79 private String username;
82 public void setSecurityContext(SecurityContext context) {
83 if (context != null && context.getUserPrincipal() != null) {
84 username = context.getUserPrincipal().getName();
88 protected String getUserName() {
92 private void handleNameMismatch(String name, String nameinURL) {
93 if (name == null || nameinURL == null) {
94 throw new BadRequestException(RestMessages.INVALIDDATA.toString() + " : Name is null");
97 if (name.equalsIgnoreCase(nameinURL)) {
100 throw new ResourceConflictException(RestMessages.INVALIDDATA.toString()
101 + " : Table Name in URL does not match the row name in request body");
105 * Create a Row for Open_vSwitch schema
107 * @param nodeType type of node e.g OVS
108 * @param nodeId ID of the node
109 * @param tableName name of the OVSDB table
110 * @param rowJson the {@link OvsdbRow} Row that is being inserted
112 * @return Response as dictated by the HTTP Response Status code
117 * Create a Bridge Row:
121 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/bridge/rows
128 * "datapath_type": "OPENFLOW"
139 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/port/rows
143 * "parent_uuid": "b01cd26b-9c63-4216-8cf2-55f7087adab1",
149 * "00:00:00:00:00:01"
161 * Create an Interface Row:
165 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/interface/rows
169 * "parent_uuid": "c7b54c9b-9b25-4801-a81d-d7bc489d4840",
175 * "00:00:bb:bb:00:01"
177 * "admin_state": "up"
188 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/SSL/rows
195 * "ca_cert": "ca_cert",
196 * "bootstrap_ca_cert": true,
197 * "certificate": "pieceofpaper",
198 * "private_key": "private"
205 * Create an sFlow Row:
209 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/sflow/rows
213 * "parent_uuid": "6b3072ba-a120-4db9-82f8-a8ce4eae6942",
234 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/qos/rows
238 * "parent_uuid": "b109dbcf-47bb-4121-b244-e623b3421d6e",
241 * "type": "linux-htb"
248 * Create a Queue Row:
252 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/queue/rows
255 * "parent_uuid": "b16eae7d-7e97-46d2-95d1-333d1de4a3d7",
268 * Create a Netflow Row:
272 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/netflow/rows
276 * "parent_uuid": "b01cd26b-9c63-4216-8cf2-55f7087adab1",
282 * "192.168.1.102:9998"
285 * "active_timeout": "0"
292 * Create a Manager Row:
296 * POST http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/manager/rows
300 * "parent_uuid": "8d3fb89b-5fac-4631-a990-f5a4e7f5383a",
303 * "target": "a_string",
304 * "is_connected": true,
310 * @throws IOException
311 * @throws ExecutionException
312 * @throws InterruptedException
315 @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows")
317 @StatusCodes({ @ResponseCode(code = 201, condition = "Row Inserted successfully"),
318 @ResponseCode(code = 400, condition = "Invalid data passed"),
319 @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
320 @Consumes({ MediaType.APPLICATION_JSON})
321 public Response addRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
322 @PathParam("tableName") String tableName, JsonNode rowJson) throws IOException, InterruptedException, ExecutionException {
324 if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
325 throw new UnauthorizedException("User is not authorized to perform this operation");
328 OvsdbConfigurationService
329 ovsdbTable = (OvsdbConfigurationService)ServiceHelper.getGlobalInstance(OvsdbConfigurationService.class,
331 if (ovsdbTable == null) {
332 throw new ServiceUnavailableException("OVS Configuration Service " + RestMessages.SERVICEUNAVAILABLE.toString());
335 OvsdbConnectionService
336 connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
337 Node node = connectionService.getNode(nodeId);
339 OvsdbClient client = connectionService.getConnection(node).getClient();
340 OvsdbRow localRow = OvsdbRow.fromJsonNode(client, OvsVswitchdSchemaConstants.DATABASE_NAME, rowJson);
341 String bckCompatibleTableName = this.getBackwardCompatibleTableName(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
343 if (localRow == null) {
344 return Response.status(Response.Status.BAD_REQUEST).build();
348 statusWithUuid = ovsdbTable.insertRow(node, bckCompatibleTableName, localRow.getParentUuid(), localRow.getRow());
350 if (statusWithUuid.isSuccess()) {
351 UUID uuid = statusWithUuid.getUuid();
352 return Response.status(Response.Status.CREATED)
353 .header("Location", String.format("%s/%s", _uriInfo.getAbsolutePath().toString(),
355 .entity(uuid.toString())
358 return NorthboundUtils.getResponse(
359 new org.opendaylight.controller.sal.utils.Status(
360 org.opendaylight.controller.sal.utils.StatusCode.SUCCESS));
366 * @param nodeType type of node e.g OVS
367 * @param nodeId ID of the node
368 * @param tableName name of the ovsdb table
369 * @param rowUuid UUID of the row being read
371 * @return Row corresponding to the UUID.
377 * Get a specific Bridge Row:
378 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/bridge/rows/6f4c602c-026f-4390-beea-d50d6d448100
380 * Get a specific Port Row:
381 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/port/rows/6f4c602c-026f-4390-beea-d50d6d448100
383 * Get a specific Interface Row:
384 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/interface/rows/6f4c602c-026f-4390-beea-d50d6d448100
386 * Get a specific Controller Row:
387 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/controller/rows/6f4c602c-026f-4390-beea-d50d6d448100
389 * Get a specific SSL Row:
390 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/SSL/rows/6f4c602c-026f-4390-beea-d50d6d448100
392 * Get a specific sFlow Row:
393 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/sflow/rows/6f4c602c-026f-4390-beea-d50d6d448100
395 * Get a specific QoS Row:
396 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/qos/rows/6f4c602c-026f-4390-beea-d50d6d448100
398 * Get a specific Queue Row:
399 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/queue/rows/6f4c602c-026f-4390-beea-d50d6d448100
401 * Get a specific Netflow Row:
402 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/netflow/rows/6f4c602c-026f-4390-beea-d50d6d448100
404 * Get a specific Manager Row:
405 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/manager/rows/6f4c602c-026f-4390-beea-d50d6d448100
409 @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows/{rowUuid}")
411 @StatusCodes({ @ResponseCode(code = 200, condition = "Row Updated successfully"),
412 @ResponseCode(code = 400, condition = "Invalid data passed"),
413 @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
414 @Produces({ MediaType.APPLICATION_JSON})
416 public Row getRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
417 @PathParam("tableName") String tableName, @PathParam("rowUuid") String rowUuid) {
419 if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
420 throw new UnauthorizedException("User is not authorized to perform this operation");
423 OvsdbConfigurationService
424 ovsdbTable = (OvsdbConfigurationService)ServiceHelper.getGlobalInstance(OvsdbConfigurationService.class,
426 if (ovsdbTable == null) {
427 throw new ServiceUnavailableException("UserManager " + RestMessages.SERVICEUNAVAILABLE.toString());
430 OvsdbConnectionService
431 connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
432 Node node = connectionService.getNode(nodeId);
433 OvsdbClient client = connectionService.getConnection(node).getClient();
434 String bckCompatibleTableName = this.getBackwardCompatibleTableName(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
438 row = ovsdbTable.getRow(node, bckCompatibleTableName, rowUuid);
439 } catch (Exception e) {
440 throw new BadRequestException(e.getMessage());
446 * Read all Rows of a table
448 * @param nodeType type of node e.g OVS
449 * @param nodeId ID of the node
450 * @param tableName name of the ovsdb table
452 * @return All the Rows of a table
458 * Get all Bridge Rows:
459 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/bridge/rows
462 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/port/rows
464 * Get all Interface Rows:
465 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/interface/rows
467 * Get all Controller Rows:
468 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/controller/rows
471 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/SSL/rows
473 * Get all sFlow Rows:
474 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/sflow/rows
477 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/qos/rows
479 * Get all Queue Rows:
480 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/queue/rows
482 * Get all Netflow Rows:
483 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/netflow/rows
485 * Get all Manager Rows:
486 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/manager/rows
488 * Get all Open vSwitch Rows:
489 * GET http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/open_vswitch/rows
493 @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows")
495 @StatusCodes({ @ResponseCode(code = 200, condition = "Row Updated successfully"),
496 @ResponseCode(code = 400, condition = "Invalid data passed"),
497 @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
498 @Produces({ MediaType.APPLICATION_JSON})
499 @TypeHint(OvsdbRows.class)
500 public OvsdbRows getAllRows(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
501 @PathParam("tableName") String tableName) {
502 if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
503 throw new UnauthorizedException("User is not authorized to perform this operation");
506 OvsdbConfigurationService
507 ovsdbTable = (OvsdbConfigurationService)ServiceHelper.getGlobalInstance(OvsdbConfigurationService.class,
509 if (ovsdbTable == null) {
510 throw new ServiceUnavailableException("UserManager " + RestMessages.SERVICEUNAVAILABLE.toString());
513 OvsdbConnectionService
514 connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
515 Node node = connectionService.getNode(nodeId);
516 OvsdbClient client = connectionService.getConnection(node).getClient();
517 String bckCompatibleTableName = this.getBackwardCompatibleTableName(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
518 Map<String, Row> rows = null;
520 rows = ovsdbTable.getRows(node, bckCompatibleTableName);
521 } catch (Exception e) {
522 throw new BadRequestException(e.getMessage());
524 return new OvsdbRows(rows);
531 * @param nodeType type of node e.g OVS
532 * @param nodeId ID of the node
533 * @param tableName name of the ovsdb table
534 * @param rowUuid UUID of the row being updated
535 * @param row the {@link OVSDBRow} Row that is being updated
537 * @return Response as dictated by the HTTP Response Status code
542 * Update the Bridge row to add a controller
546 * PUT http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/bridge/rows/b01cd26b-9c63-4216-8cf2-55f7087adab1
557 * "a566e8b4-fc38-499b-8623-6087d5b36b72"
567 @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows/{rowUuid}")
569 @StatusCodes({ @ResponseCode(code = 200, condition = "Row Updated successfully"),
570 @ResponseCode(code = 400, condition = "Invalid data passed"),
571 @ResponseCode(code = 401, condition = "User not authorized to perform this operation")})
572 @Consumes({ MediaType.APPLICATION_JSON})
573 public Response updateRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
574 @PathParam("tableName") String tableName, @PathParam("rowUuid") String rowUuid,
577 if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
578 throw new UnauthorizedException("User is not authorized to perform this operation");
581 OvsdbConfigurationService
582 ovsdbTable = (OvsdbConfigurationService)ServiceHelper.getGlobalInstance(OvsdbConfigurationService.class,
584 if (ovsdbTable == null) {
585 throw new ServiceUnavailableException("OVS Configuration Service " + RestMessages.SERVICEUNAVAILABLE.toString());
588 OvsdbConnectionService
589 connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
590 Node node = connectionService.getNode(nodeId);
591 OvsdbClient client = connectionService.getConnection(node).getClient();
592 String bckCompatibleTableName = this.getBackwardCompatibleTableName(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
593 OvsdbRow localRow = OvsdbRow.fromJsonNode(client, OvsVswitchdSchemaConstants.DATABASE_NAME, rowJson);
595 if (localRow == null) {
596 return Response.status(Response.Status.BAD_REQUEST).build();
599 Status status = ovsdbTable.updateRow(node, bckCompatibleTableName, localRow.getParentUuid(), rowUuid, localRow.getRow());
600 return NorthboundUtils.getResponse(
601 new org.opendaylight.controller.sal.utils.Status(
602 org.opendaylight.controller.sal.utils.StatusCode.SUCCESS));
608 * @param nodeType type of node e.g OVS
609 * @param nodeId ID of the node
610 * @param tableName name of the ovsdb table
611 * @param uuid UUID of the Row to be removed
613 * @return Response as dictated by the HTTP Response Status code
619 * Delete a specific Bridge Row:
620 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/bridge/rows/6f4c602c-026f-4390-beea-d50d6d448100
622 * Delete a specific Port Row:
623 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/port/rows/6f4c602c-026f-4390-beea-d50d6d448100
625 * Delete a specific Interface Row:
626 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/interface/rows/6f4c602c-026f-4390-beea-d50d6d448100
628 * Delete a specific Controller Row:
629 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/controller/rows/6f4c602c-026f-4390-beea-d50d6d448100
631 * Delete a specific SSL Row:
632 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/SSL/rows/6f4c602c-026f-4390-beea-d50d6d448100
634 * Delete a specific sFlow Row:
635 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/sflow/rows/6f4c602c-026f-4390-beea-d50d6d448100
637 * Delete a specific QoS Row:
638 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/qos/rows/6f4c602c-026f-4390-beea-d50d6d448100
640 * Delete a specific Queue Row:
641 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/queue/rows/6f4c602c-026f-4390-beea-d50d6d448100
643 * Delete a specific Netflow Row:
644 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/netflow/rows/6f4c602c-026f-4390-beea-d50d6d448100
646 * Delete a specific Manager Row:
647 * DELETE http://localhost:8080/ovsdb/nb/v2/node/OVS/HOST1/tables/manager/rows/6f4c602c-026f-4390-beea-d50d6d448100
651 @Path("/node/{nodeType}/{nodeId}/tables/{tableName}/rows/{uuid}")
653 @StatusCodes({ @ResponseCode(code = 204, condition = "User Deleted Successfully"),
654 @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
655 @ResponseCode(code = 404, condition = "The userName passed was not found"),
656 @ResponseCode(code = 500, condition = "Internal Server Error : Removal of user failed"),
657 @ResponseCode(code = 503, condition = "Service unavailable") })
658 public Response removeRow(@PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
659 @PathParam("tableName") String tableName, @PathParam("uuid") String uuid) {
660 if (!NorthboundUtils.isAuthorized(getUserName(), "default", Privilege.WRITE, this)) {
661 throw new UnauthorizedException("User is not authorized to perform this operation");
664 OvsdbConfigurationService
665 ovsdbTable = (OvsdbConfigurationService)ServiceHelper.getGlobalInstance(OvsdbConfigurationService.class,
667 if (ovsdbTable == null) {
668 throw new ServiceUnavailableException("OVS Configuration Service " + RestMessages.SERVICEUNAVAILABLE.toString());
671 OvsdbConnectionService
672 connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
673 Node node = connectionService.getNode(nodeId);
674 OvsdbClient client = connectionService.getConnection(node).getClient();
675 String bckCompatibleTableName = this.getBackwardCompatibleTableName(client, OvsVswitchdSchemaConstants.DATABASE_NAME, tableName);
677 Status status = ovsdbTable.deleteRow(node, bckCompatibleTableName, uuid);
678 if (status.isSuccess()) {
679 return Response.noContent().build();
681 return NorthboundUtils.getResponse(
682 new org.opendaylight.controller.sal.utils.Status(
683 org.opendaylight.controller.sal.utils.StatusCode.SUCCESS));
686 private String getBackwardCompatibleTableName(OvsdbClient client, String databaseName, String tableName) {
687 DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
688 if (dbSchema == null || tableName == null) return tableName;
689 for (String dbTableName : dbSchema.getTables()) {
690 if (dbTableName.equalsIgnoreCase(tableName)) return dbTableName;