You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@warble.apache.org by hu...@apache.org on 2018/06/29 20:44:21 UTC

[incubator-warble-server] branch master updated (8880aeb -> 1298ba7)

This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git.


    from 8880aeb  tweak formatting and wording
     new 6f50d4b  prime setup with new dbs for tasks
     new 59b6df2  add task registry plugin for server
     new 2cbd0c0  first stab at a task list API endpoint
     new b737d78  regen openapi yaml
     new 1298ba7  add in new custom response codes

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 api/handler.py          |   2 +
 api/pages/node/tasks.py | 116 +++++++++++++++++++++++
 api/plugins/tasks.py    | 241 ++++++++++++++++++++++++++++++++++++++++++++++++
 api/yaml/openapi.yaml   |  16 ++++
 setup/dbs.yaml          |  32 +++++++
 5 files changed, 407 insertions(+)
 create mode 100644 api/pages/node/tasks.py
 create mode 100644 api/plugins/tasks.py


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org


[incubator-warble-server] 04/05: regen openapi yaml

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git

commit b737d78f64e71227ed40b357ca4d51ced40adb6d
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Fri Jun 29 15:43:54 2018 -0500

    regen openapi yaml
---
 api/yaml/openapi.yaml | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/api/yaml/openapi.yaml b/api/yaml/openapi.yaml
index 15c9816..8c164e2 100644
--- a/api/yaml/openapi.yaml
+++ b/api/yaml/openapi.yaml
@@ -479,6 +479,22 @@ paths:
                 $ref: '#/components/schemas/Error'
           description: unexpected error
       summary: Displays the current status of a node
+  /api/node/tasks:
+    get:
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/TaskList'
+          description: Node task list
+        default:
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+          description: unexpected error
+      summary: Returns a list of tasks assigned to a given node
   /api/session:
     delete:
       requestBody:


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org


[incubator-warble-server] 03/05: first stab at a task list API endpoint

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git

commit 2cbd0c0770440f12e33abc0d12a6a2de3e9eabd5
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Fri Jun 29 15:43:46 2018 -0500

    first stab at a task list API endpoint
    
    For clients (agents/nodes), the task list is encrypted using the
    client's public key. for normal users, it is plain text, with various
    bits redacted unless the user has access to it.
---
 api/pages/node/tasks.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/api/pages/node/tasks.py b/api/pages/node/tasks.py
new file mode 100644
index 0000000..44c6aef
--- /dev/null
+++ b/api/pages/node/tasks.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+# OPENAPI-URI: /api/node/tasks
+########################################################################
+# get:
+#   responses:
+#     '200':
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/TaskList'
+#       description: Node task list
+#     default:
+#       content:
+#         application/json:
+#           schema:
+#             $ref: '#/components/schemas/Error'
+#       description: unexpected error
+#   summary: Returns a list of tasks assigned to a given node
+# 
+########################################################################
+
+
+
+
+
+"""
+This is the node task list handler for Apache Warble
+"""
+
+import json
+import plugins.crypto
+import plugins.registry
+import plugins.tasks
+import base64
+
+def run(API, environ, indata, session):
+    
+    method = environ['REQUEST_METHOD']
+    
+    # Fetch list of tasks to perform
+    if method == "GET":
+        
+        # Fetching tasks for a node?
+        if session.client:
+            if not session.client.verified:
+                raise API.exception(403, "This client node has not been verified on master yet.")
+            if not session.client.enabled:
+                raise API.exception(444, "This client node has been disabled, no tasks will be sent.")
+            # Get all tasks that are enabled
+            tasks = plugins.tasks.all(session)
+            tasklist = []
+            for task in tasks:
+                if task.enabled:
+                    # Don't show tests that are not assigned to this node
+                    if 'nodes' in task.payload and session.client.id not in task.payload['nodes']:
+                        continue
+                    tasklist.append({
+                        'id': task.id,
+                        'name': task.name,
+                        'payload': task.payload
+                    })
+            # This is the fun part! Because $design, we have to encrypt using the client's public key!
+            # This way, only the _true_ client can decrypt it, and no snooping.
+            plain = json.dumps({
+                'tasks': tasklist
+                }, indent = 2)
+            crypted = plugins.crypto.encrypt(session.client.key, plain)
+            cryptbase = base64.b64encode(crypted)
+            yield cryptbase
+            return
+        
+        # Or are we fetching tasks for a user?
+        elif session.user:
+            tasks = plugins.tasks.all(session)
+            acl = plugins.tasks.cataccess(session)
+            tasklist = []
+            for task in tasks:
+                # Only show task if super user or ACL allows access
+                canwrite = True if session.user['userlevel'] == 'superuser' or (task.category in acl and acl[task.id] > 1) else False
+                canread = True if task.category in acl or session.user['userlevel'] == 'superuser' else False
+                if canread:
+                    tasklist.append({
+                        'id': task.id,
+                        'category': task.category,
+                        'enabled': task.enabled,
+                        'muted': task.muted,
+                        'name': task.name,
+                        'payload': task.payload if canwrite else None
+                    })
+                        
+            yield json.dumps({
+                'tasks': tasklist
+                }, indent = 2)
+            return
+        else:
+            raise API.exception(499, "Unknown API Key passed by client")
+
+    # Finally, if we hit a method we don't know, balk!
+    yield API.exception(400, "I don't know this request method!!")
+    


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org


[incubator-warble-server] 01/05: prime setup with new dbs for tasks

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git

commit 6f50d4b7abe76af59e13336ea8e52acf4f560372
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Fri Jun 29 15:42:36 2018 -0500

    prime setup with new dbs for tasks
---
 setup/dbs.yaml | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/setup/dbs.yaml b/setup/dbs.yaml
index 95cf664..df3da3d 100644
--- a/setup/dbs.yaml
+++ b/setup/dbs.yaml
@@ -33,3 +33,35 @@ registry:
     lastping:     integer   # Last time node was alive
     version:      text      # Node client software version
 
+# Task registry for nodes
+nodetasks:
+  driver: sqlite
+  path: nodetasks.db
+  layout:
+    id:           integer primary key   # ID of task
+    type:         text                  # Task type (test class, so nodes know whether they support it or not)
+    enabled:      boolean               # Whether task is currently enabled or not
+    muted:        boolean               # Whether task alerting is currently muted or not (does not impact testing, only alerting)
+    category:     integer               # Category ID for task (0 means no category/group)
+    name:         text                  # Name (short description) of task
+    payload:      text                  # Task payload (JSON blob to allow custom content)
+
+# Task groups for nodes
+nodecats:
+  driver: sqlite
+  path:   nodecats.db
+  layout:
+    id:           integer primary key   # ID of category
+    name:         text                  # Name of category
+    description:  text                  # Short description of category
+    settings:     text                  # Notification settings (JSON blob to allow for customizations)
+
+# User -> Task category mappings
+nodeacl:
+  driver: sqlite
+  path:   nodeacl.db
+  layout:
+    userid:       text            # username
+    catid:        integer         # task category id
+    access:       integer         # category access level (0 = none, 1 = read, 2 = read/write, 3 = admin)
+


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org


[incubator-warble-server] 05/05: add in new custom response codes

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git

commit 1298ba74276e9bf48e8a5fbd350ff4bf181c01d8
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Fri Jun 29 15:44:07 2018 -0500

    add in new custom response codes
---
 api/handler.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/api/handler.py b/api/handler.py
index 03b4d5c..d22165f 100644
--- a/api/handler.py
+++ b/api/handler.py
@@ -118,6 +118,8 @@ class WarbleAPIWrapper:
                 errHeaders = {
                     403: '403 Authentication failed',
                     404: '404 Resource not found',
+                    444: '444 Empty response',
+                    499: '499 Required Token Missing',
                     500: '500 Internal Server Error',
                     501: '501 Gateway error'
                 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org


[incubator-warble-server] 02/05: add task registry plugin for server

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-warble-server.git

commit 59b6df2e8c47c1011052c6ec250b721583bfb533
Author: Daniel Gruno <hu...@apache.org>
AuthorDate: Fri Jun 29 15:43:02 2018 -0500

    add task registry plugin for server
---
 api/plugins/tasks.py | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 241 insertions(+)

diff --git a/api/plugins/tasks.py b/api/plugins/tasks.py
new file mode 100644
index 0000000..52fbcd9
--- /dev/null
+++ b/api/plugins/tasks.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+########################################################################
+
+"""
+This is the node task registry class for Apache Warble
+"""
+
+import uuid
+import re
+import time
+import json
+
+""" Warble task class """
+class task(object):
+    
+    def __init__(self, session, taskid = None, taskrow = None):
+        """ Loads a task from the registry or inits a new one """
+        self._data = {}
+        self.session = session
+        self.conn = session.DB.sqlite.open('nodetasks.db')
+        
+        # task variables
+        self.id = None
+        self.type = None
+        self.category = 0
+        self.enabled = True
+        self.muted = False
+        self.payload = {}
+        self.name = None
+        
+        # Load a task by ID
+        if taskid:
+            doc = None
+            nc = self.conn.cursor()
+            # Load by Task ID?
+            if re.match(r"^[0-9]+$", str(taskid)):
+                self.id = int(taskid)
+                nc.execute("SELECT * FROM `tasks` WHERE `id` = ? LIMIT 1", (taskid,))
+                doc = nc.fetchone()
+                
+            if doc:
+                self.id = doc['id']
+                self.type = doc['type']
+                self.category = doc['category']
+                self.enabled = True if doc['enabled'] == 1 else False
+                self.muted = True if doc['muted'] == 1 else False
+                self.payload = json.loads(doc['payload'])
+                self.ipv6 = False # TODO!
+                self.name = doc['name']
+            else:
+                raise Exception("No such task found in registry")
+        
+        # Or load a task by data row?
+        elif taskrow:
+            doc = taskrow
+            self.id = doc['id']
+            self.type = doc['type']
+            self.category = doc['category']
+            self.enabled = True if doc['enabled'] == 1 else False
+            self.muted = True if doc['muted'] == 1 else False
+            self.payload = json.loads(doc['payload'])
+            self.ipv6 = False # TODO!
+            self.name = doc['name']
+        
+        
+    def save(self):
+        """ Saves or updates a task in the registry """
+        nc = self.conn.cursor()
+        # Save a new task?
+        if not self.id:
+            nc.execute("INSERT INTO `tasks` (`type`, `category`, `enabled`, `muted`, `payload`, `name`) VALUES (?, ?, ?, ?, ?, ?)",
+                    (self.type, self.category, 1 if self.enabled else 0, 1 if self.muted else 0, json.dumps(self.payload), self.name, )
+                )
+        # Update existing task?
+        else:
+            nc.execute("UPDATE `tasks` SET `type` = ?, `category` = ?, `enabled` = ?, `muted` = ?, `payload` = ?, `name` = ? WHERE `id` = ? LIMIT 1",
+                    (self.type, self.category, 1 if self.enabled else 0, 1 if self.muted else 0, json.dumps(self.payload), self.name, self.id, )
+                )
+        self.conn.commit()
+    
+    def remove(self):
+        """ Removes a task from the registry """
+        nc = self.conn.cursor()
+        if self.id:
+            nc.execute("DELETE FROM `tasks` WHERE `id` = ? LIMIT 1", (self.id, ))
+            self.conn.commit()
+            
+    def accesslevel(self, user):
+        """ Determines if a user can view/edit a task or not """
+        aclcon = session.DB.sqlite.open('nodeacl.db')
+        cur = aclcon.cursor()
+        cur.execute("SELECT * FROM `nodeacl` WHERE `catid` = ? AND `userid` = ? LIMIT 1", (self.category, user['userid'], ))
+        acl = cur.fetchone()
+        if acl:
+            return ['none', 'read', 'write', 'admin'][acl['access']]
+        else:
+            return 'none'
+        aclcon.close()
+        
+    def __del__(self):
+        # shut off sqlite connection
+        if self.conn:
+            self.conn.close()
+
+    def __enter__(self):
+        pass
+    def __exit__(self, exception_type, exception_value, traceback):
+        del self
+        
+        
+"""
+  id:           integer primary key   # ID of category
+    name:         text                  # Name of category
+    description:  text                  # Short description of category
+    settings:     text                  # Notification settings (JSON blob to allow for customizations)
+"""
+    
+""" Warble task category class """
+class category(object):
+    
+    def __init__(self, session, catid = None):
+        """ Loads a category from the registry or inits a new one """
+        self._data = {}
+        self.session = session
+        self.conn = session.DB.sqlite.open('nodecats.db')
+        
+        # category variables
+        self.id = None
+        self.name = None
+        self.description = None
+        self.settings = {}
+        self.tasks = []
+        
+        # Load existing category?
+        if catid:
+            doc = None
+            nc = self.conn.cursor()
+            # Load by Cat ID?
+            if re.match(r"^[0-9]+$", str(catid)):
+                self.id = int(catid)
+                nc.execute("SELECT * FROM `nodecats` WHERE `id` = ? LIMIT 1", (catid,))
+                doc = nc.fetchone()
+            if doc:
+                self.id = doc['id']
+                self.name = doc['name']
+                self.description = doc['description']
+                self.settings = json.loads(doc['settings'])
+                
+                # Load tasks in category
+                taskcon = session.DB.sqlite.open('nodetasks.db')
+                cur = taskcon.cursor()
+                cur.execute("SELECT * FROM `nodetasks` WHERE `category` = ?", (self.id, ))
+                for row in cur.fetchall():
+                    t = task(session, taskrow = row)
+                    self.tasks.append(t)
+                taskcon.close()
+            else:
+                raise Exception("No such category found in registry")
+        
+        
+    def save(self):
+        """ Saves or updates a category in the registry """
+        nc = self.conn.cursor()
+        # Save a new category?
+        if not self.id:
+            nc.execute("INSERT INTO `nodecats` (`name`, `description`, `settings`) VALUES (?, ?, ?)",
+                    (self.name, self.description, json.dumps(self.settings), )
+                )
+        # Update existing category?
+        else:
+            nc.execute("UPDATE `nodecats` SET `name` = ?, `description` = ?, `settings` = ? WHERE `id` = ? LIMIT 1",
+                    (self.name, self.description, json.dumps(self.settings), self.id, )
+                )
+        self.conn.commit()
+    
+    def remove(self):
+        """ Removes a category from the registry """
+        nc = self.conn.cursor()
+        if self.id:
+            nc.execute("DELETE FROM `nodecats` WHERE `id` = ? LIMIT 1", (self.id, ))
+            self.conn.commit()
+            
+    def accesslevel(self, user):
+        """ Determines if a user can view/edit a category or not """
+        aclcon = session.DB.sqlite.open('nodeacl.db')
+        cur = aclcon.cursor()
+        cur.execute("SELECT * FROM `nodeacl` WHERE `catid` = ? AND `userid` = ? LIMIT 1", (self.id, user['userid'], ))
+        acl = cur.fetchone()
+        if acl:
+            return ['none', 'read', 'write', 'admin'][acl['access']]
+        else:
+            return 'none'
+        aclcon.close()
+        
+    def __del__(self):
+        # shut off sqlite connection
+        if self.conn:
+            self.conn.close()
+
+    def __enter__(self):
+        pass
+    def __exit__(self, exception_type, exception_value, traceback):
+        del self
+    
+# Wrapper for getting all tasks:
+def all(session):
+    tlist = []
+    taskcon = session.DB.sqlite.open('nodetasks.db')
+    cur = taskcon.cursor()
+    cur.execute("SELECT * FROM `nodetasks` WHERE 1")
+    for row in cur.fetchall():
+        t = task(session, taskrow = row)
+        tlist.append(t)
+    taskcon.close()
+    return tlist
+
+# Wrapper for getting ACL for a user
+def cataccess(session):
+    aclcon = session.DB.sqlite.open('nodeacl.db')
+    cur = aclcon.cursor()
+    cur.execute("SELECT * FROM `nodeacl` WHERE `userid` = ? LIMIT 1", (session.user['userid'], ))
+    acl = {}
+    for row in cur.fetchall():
+        acl[row['catid']] = ['none', 'read', 'write', 'admin'][row['access']]
+    aclcon.close()
+    return acl


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@warble.apache.org
For additional commands, e-mail: commits-help@warble.apache.org