Added support for Grafana data source for WebAPI
authorEriks Jenkevics <eriks@netxms.org>
Tue, 14 Mar 2017 11:58:30 +0000 (13:58 +0200)
committerEriks Jenkevics <eriks@netxms.org>
Tue, 14 Mar 2017 11:58:36 +0000 (13:58 +0200)
android/src/console/app/src/main/java/org/netxms/ui/android/main/activities/NodePollerActivity.java
src/server/nxapisrv/java/pom.xml
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/WebSvcApplication.java
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/WebSvcStatusService.java
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/AbstractHandler.java
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/AlarmsGrafana.java [new file with mode: 0644]
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/Sessions.java
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/json/JsonTools.java

index d1a2929..2e9fd0a 100644 (file)
@@ -94,6 +94,7 @@ public class NodePollerActivity extends AbstractClientActivity
 
                                                @Override
                                                public void setStreamId(long l)
+
                                                {
                                                }
                                        });
index db0c7d9..46ade03 100644 (file)
       <dependency>
          <groupId>org.restlet.jee</groupId>
          <artifactId>org.restlet</artifactId>
-         <version>2.2.2</version>
+         <version>2.3.9</version>
       </dependency>
       <dependency>
          <groupId>org.restlet.jee</groupId>
          <artifactId>org.restlet.ext.json</artifactId>
-         <version>2.2.2</version>
+         <version>2.3.9</version>
       </dependency>
       <dependency>
          <groupId>org.restlet.jee</groupId>
          <artifactId>org.restlet.ext.servlet</artifactId>
-         <version>2.2.2</version>
+         <version>2.3.9</version>
          <scope>runtime</scope>
       </dependency>
       <dependency>
index a3d3c64..f787272 100644 (file)
  */
 package org.netxms.websvc;
 
+import java.util.Arrays;
+import java.util.HashSet;
 import org.netxms.websvc.handlers.Alarms;
+import org.netxms.websvc.handlers.AlarmsGrafana;
 import org.netxms.websvc.handlers.Objects;
 import org.netxms.websvc.handlers.Sessions;
 import org.restlet.Application;
 import org.restlet.Restlet;
+import org.restlet.data.Method;
 import org.restlet.routing.Router;
+import org.restlet.service.CorsService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,6 +44,13 @@ public class WebSvcApplication extends Application
     */
    public WebSvcApplication()
    {
+      CorsService corsService = new CorsService();
+      corsService.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
+      corsService.setAllowedHeaders(new HashSet<String>(Arrays.asList("Content-Type", "X-NXCookie")));
+      corsService.setDefaultAllowedMethods(new HashSet<Method>(
+                                                Arrays.asList(Method.POST, Method.GET, Method.OPTIONS,
+                                                              Method.PUT, Method.DELETE)));
+      getServices().add(corsService);
       setStatusService(new WebSvcStatusService());
    }
    
@@ -52,6 +64,7 @@ public class WebSvcApplication extends Application
       Router router = new Router(getContext());
       router.attach("/alarms", Alarms.class);
       router.attach("/alarms/{id}", Alarms.class);
+      router.attach("/grafana/alarms", AlarmsGrafana.class);
       router.attach("/objects", Objects.class);
       router.attach("/objects/{id}", Objects.class);
       router.attach("/sessions", Sessions.class);
index a38cb6a..1f84594 100644 (file)
@@ -27,10 +27,10 @@ public class WebSvcStatusService extends StatusService
    private Logger log = LoggerFactory.getLogger(WebSvcStatusService.class);
    
    /* (non-Javadoc)
-    * @see org.restlet.service.StatusService#getStatus(java.lang.Throwable, org.restlet.Request, org.restlet.Response)
+    * @see org.restlet.service.StatusService#toStatus(java.lang.Throwable, org.restlet.Request, org.restlet.Response)
     */
    @Override
-   public Status getStatus(Throwable throwable, Request request, Response response)
+   public Status toStatus(Throwable throwable, Request request, Response response)
    {
       if (throwable != null)
       {
@@ -41,14 +41,14 @@ public class WebSvcStatusService extends StatusService
          }
          log.error("Exception in request handler", throwable);
       }
-      return super.getStatus(throwable, request, response);
+      return super.toStatus(throwable, request, response);
    }
 
    /* (non-Javadoc)
-    * @see org.restlet.service.StatusService#getStatus(java.lang.Throwable, org.restlet.resource.Resource)
+    * @see org.restlet.service.StatusService#toStatus(java.lang.Throwable, org.restlet.resource.Resource)
     */
    @Override
-   public Status getStatus(Throwable throwable, Resource resource)
+   public Status toStatus(Throwable throwable, Resource resource)
    {
       if (throwable != null)
       {
@@ -59,7 +59,7 @@ public class WebSvcStatusService extends StatusService
          }
          log.error("Exception in request handler", throwable);
       }
-      return super.getStatus(throwable, resource);
+      return super.toStatus(throwable, resource);
    }
    
    /**
@@ -73,7 +73,7 @@ public class WebSvcStatusService extends StatusService
       switch(rcc)
       {
          case RCC.ACCESS_DENIED:
-            return Status.CLIENT_ERROR_FORBIDDEN;
+            return Status.CLIENT_ERROR_UNAUTHORIZED;
          case RCC.INCOMPATIBLE_OPERATION:
             return Status.CLIENT_ERROR_METHOD_NOT_ALLOWED;
          case RCC.INTERNAL_ERROR:
index 3cb4e40..529b3d0 100644 (file)
@@ -30,6 +30,7 @@ import org.netxms.websvc.SessionToken;
 import org.netxms.websvc.WebSvcStatusService;
 import org.netxms.websvc.json.JsonTools;
 import org.restlet.Application;
+import org.restlet.data.Header;
 import org.restlet.data.MediaType;
 import org.restlet.ext.json.JsonRepresentation;
 import org.restlet.representation.Representation;
@@ -48,7 +49,7 @@ import com.google.gson.JsonObject;
  */
 public abstract class AbstractHandler extends ServerResource
 {
-   private Logger log = LoggerFactory.getLogger(AbstractHandler.class);
+   protected Logger log = LoggerFactory.getLogger(AbstractHandler.class);
    private SessionToken sessionToken = null;
    private NXCSession session = null;
    
@@ -136,12 +137,13 @@ public abstract class AbstractHandler extends ServerResource
     * @return
     */
    @Delete
-   public Representation onDelete() throws Exception
+   public Representation onDelete(Representation entity) throws Exception
    {
       String id = getEntityId();
+      
       log.debug("DELETE: entityId = " + id);
       if (attachToSession())
-      {
+      {         
          Object response = (id != null) ? delete(id) : createErrorResponse(RCC.INCOMPATIBLE_OPERATION);
          return new StringRepresentation(JsonTools.jsonFromObject(response), MediaType.APPLICATION_JSON);
       }
@@ -166,30 +168,53 @@ public abstract class AbstractHandler extends ServerResource
     * 
     * @return true if attached successfully
     */
-   private boolean attachToSession()
+   protected boolean attachToSession() throws Exception
+   {
+      SessionToken token = findSessionToken();
+      if ((token == null) && (getRequest().getHeaders().getFirstValue("X-Login") != null))
+      {
+         String login = getRequest().getHeaders().getFirstValue("X-Login");
+         String password = getRequest().getHeaders().getFirstValue("X-Password");
+         log.debug("Cannot find session token - re-authenticating (login=" + login + ")");
+         token = login(login, password);
+         getResponse().getHeaders().add(new Header("X-SessionId", token.getGuid().toString()));
+      }
+      
+      if (token != null)
+      {
+         log.debug("Handler attached to session " + token.getGuid());
+         sessionToken = token;
+         session = token.getSession();
+      }
+      else
+      {
+         log.debug("Session token not found and new session cannot be created");
+      }
+      return session != null;
+   }
+   
+   /**
+    * Find session token for this session
+    * 
+    * @return session token or null
+    */
+   protected SessionToken findSessionToken()
    {
-      SessionToken token = null;
       String cookie = getCookies().getValues("session_handle");
       if (cookie != null)
       {
          UUID guid = UUID.fromString(cookie);
-         token = SessionStore.getInstance(getServletContext()).getSessionToken(guid);
-         if (token != null)
-         {
-            log.debug("Handler attached to session " + guid);
-            sessionToken = token;
-            session = token.getSession();
-         }
-         else
-         {
-            log.debug("Session token with GUID " + guid + " not found");
-         }
+         return SessionStore.getInstance(getServletContext()).getSessionToken(guid);
       }
-      else
+      
+      String sid = getRequest().getHeaders().getFirstValue("X-SessionId");
+      if (sid != null && !sid.equals("0"))
       {
-         log.debug("Session handle cookie missing in request");
+         UUID guid = UUID.fromString(sid);
+         return SessionStore.getInstance(getServletContext()).getSessionToken(guid);
       }
-      return session != null;
+      
+      return null;
    }
    
    /**
@@ -300,4 +325,18 @@ public abstract class AbstractHandler extends ServerResource
    {
       return createErrorResponse(RCC.INCOMPATIBLE_OPERATION);
    }
+   
+   /**
+    * Login to NetXMS server
+    * @param login
+    * @param password
+    * @return SessionToken
+    */
+   protected SessionToken login(String login, String password) throws Exception
+   {
+      session = new NXCSession("127.0.0.1");
+      session.connect();
+      session.login(login, (password == null) ? "" : password);
+      return SessionStore.getInstance(getServletContext()).registerSession(session);
+   }
 }
diff --git a/src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/AlarmsGrafana.java b/src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/AlarmsGrafana.java
new file mode 100644 (file)
index 0000000..fd71397
--- /dev/null
@@ -0,0 +1,104 @@
+package org.netxms.websvc.handlers;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+import org.json.JSONException;
+import org.netxms.client.NXCSession;
+import org.netxms.client.events.Alarm;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.client.users.AbstractUserObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+public class AlarmsGrafana extends AbstractHandler
+{
+   private String[] states = { "Outstanding", "Acknowledged", "Resolved", "Terminated" };
+   private Logger log = LoggerFactory.getLogger(AlarmsGrafana.class);
+   
+   /* (non-Javadoc)
+    * @see org.netxms.websvc.handlers.AbstractHandler#getCollection(java.util.Map)
+    */
+   @Override
+   public Object getCollection(Map<String, String> query) throws Exception
+   {
+      JsonObject root = new JsonObject();
+
+      JsonArray columns = new JsonArray();
+      columns.add(createColumn("Severity", true, true));
+      columns.add(createColumn("State", true, false));
+      columns.add(createColumn("Source", true, false));
+      columns.add(createColumn("Message", true, false));
+      columns.add(createColumn("Count", true, false));
+      //columns.add(createColumn("Comments", true, false));
+      columns.add(createColumn("Helpdesk ID", true, false));
+      columns.add(createColumn("Ack/Resolved By", true, false));
+      columns.add(createColumn("Created", true, false));
+      columns.add(createColumn("Last Change", true, false));
+      root.add("columns", columns);
+      log.error(root.toString());
+      
+      JsonArray rows = new JsonArray();
+      
+      JsonArray r = new JsonArray();
+      DateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
+      AbstractObject object = null;
+      AbstractUserObject user = null;
+
+      NXCSession session = getSession();
+      if (!session.isObjectsSynchronized())
+         session.syncObjects();
+      
+      Map<Long, Alarm> alarms = session.getAlarms();
+      for( Alarm a : alarms.values())
+      {
+         r.add("<td style=\"background:rgb(0, 192, 0)\">" + a.getCurrentSeverity().name() + "</td>");
+         r.add(states[a.getState()]);
+         object = getSession().findObjectById(a.getSourceObjectId());
+         if (object == null)
+            r.add(a.getSourceObjectId());
+         else
+            r.add(object.getObjectName());
+         r.add(a.getMessage());
+         r.add(a.getRepeatCount());
+         //r.add(a.getCommentsCount());
+         r.add(a.getHelpdeskReference());
+         user = getSession().findUserDBObjectById(a.getAckByUser());
+         if (user == null)
+            r.add("");
+         else
+            r.add(user.getName());
+         r.add(df.format(a.getCreationTime()));
+         r.add(df.format(a.getLastChangeTime()));
+         rows.add(r);
+         r = new JsonArray();
+      }
+      
+      root.add("rows", rows);
+      
+      root.addProperty("type", "table");
+
+      JsonArray wrapper = new JsonArray();
+      wrapper.add(root);
+      log.error(wrapper.toString());
+      return wrapper;
+   }
+   
+   /**
+    * @param name
+    * @param sort
+    * @param desc
+    * @return
+    * @throws JSONException
+    */
+   private static JsonObject createColumn(String name, boolean sort, boolean desc) throws JSONException
+   {
+      JsonObject column = new JsonObject();
+      column.addProperty("text", name);
+      column.addProperty("sort", sort);
+      column.addProperty("sort", desc);
+      return column;
+   }
+}
index ec49291..926e413 100644 (file)
@@ -19,7 +19,6 @@
 package org.netxms.websvc.handlers;
 
 import org.json.JSONObject;
-import org.netxms.client.NXCSession;
 import org.netxms.client.constants.RCC;
 import org.netxms.websvc.SessionStore;
 import org.netxms.websvc.SessionToken;
@@ -45,7 +44,7 @@ public class Sessions extends AbstractHandler
    @Override
    @Post
    public Representation onPost(Representation entity) throws Exception
-   {
+   {      
       if (entity == null)
       {
          log.warn("No POST data in login call");
@@ -61,18 +60,14 @@ public class Sessions extends AbstractHandler
          return new StringRepresentation(createErrorResponse(RCC.ACCESS_DENIED).toString(), MediaType.APPLICATION_JSON);
       }
       
-      NXCSession session = new NXCSession("127.0.0.1");
-      session.connect();
-      session.login(login, password);
-      
-      SessionToken token = SessionStore.getInstance(getServletContext()).registerSession(session);
+      SessionToken token = login(login, password);
 
       log.info("Logged in to NetXMS server, assigned session id " + token.getGuid());
       getCookieSettings().add(new CookieSetting("session_handle", token.getGuid().toString()));
       
       JSONObject response = new JSONObject();
       response.put("session", token.getGuid().toString());
-      response.put("serverVersion", session.getServerVersion());
+      response.put("serverVersion", getSession().getServerVersion());
       return new StringRepresentation(response.toString(), MediaType.APPLICATION_JSON);
    }
 
index f608c51..11a1d5b 100644 (file)
@@ -20,6 +20,8 @@ package org.netxms.websvc.json;
 
 import java.net.InetAddress;
 import java.util.Date;
+import org.json.JSONArray;
+import org.json.JSONObject;
 import org.netxms.base.InetAddressEx;
 import org.netxms.base.annotations.Internal;
 import org.netxms.client.MacAddress;
@@ -48,6 +50,10 @@ public class JsonTools
    {
       if (object instanceof JsonObject)
          return ((JsonObject)object).toString();
+      if (object instanceof JSONObject)
+         return ((JSONObject)object).toString();
+      if (object instanceof JSONArray)
+         return ((JSONArray)object).toString();
       if (object instanceof ResponseContainer)
          return ((ResponseContainer)object).toJson();