Skip to content

Commit

Permalink
Added HTTP Proxy API service to allow command usage through HTTP. Fir…
Browse files Browse the repository at this point in the history
…st draft, merely tested. (v0.6.0)
  • Loading branch information
ronhanson committed Oct 14, 2018
1 parent 33d6bf6 commit 0108411
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 8 deletions.
3 changes: 2 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
2015-02-15 -- v0.4.2 -- Minor changes on readme mainly
2015-02-15 -- v0.4.2a -- Cosmetic changes
2015-03-17 -- v0.4.3 -- Build script update
2018-10-08 -- v0.5.0 -- Updated requirements + include SNMP Get from tbx lib + small code fixes
2018-10-08 -- v0.5.0 -- Updated requirements + include SNMP Get from tbx lib + small code fixes
2018-10-14 -- v0.6.0 -- Added HTTP Proxy API service to allow command usage through HTTP. First draft, merely tested.
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.0
0.6.0
32 changes: 32 additions & 0 deletions bin/doremiapi
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,30 @@ def cli(address, port=11730, format='text', debug=False):
exit(0)


def http(address, port=11730, http_bind='0.0.0.0', http_port=8087, debug=False):
"""
HTTP Restful API proxy server.
Allows you to create a HTTP Restful API server to proxy commands to a Doremi. You can then send the commands you want through http.
address: Address of the server
port: Port of the server
debug: Debug mode
"""
tbx.log.configure_logging_to_screen(debug)

import bottle
import dcitools.devices.doremi.http as http
api = http.HTTPProxy(address=address, port=port, debug=debug)
http.routeapp(api)

bottle.run(host=http_bind, port=http_port, debug=debug)

exit(0)


def snmplist():
"""
List available SNMP Command Keys
Expand Down Expand Up @@ -196,6 +220,14 @@ def main():
cli_parser.add_argument('--debug', action='store_true', help='Enable Debugging Output.')
cli_parser.set_defaults(func=cli)

http_parser = parsers.add_parser('http', help="HTTP Restul API proxy to Doremi API.")
http_parser.add_argument('address', help='Address of the Doremi server.')
http_parser.add_argument('--port', type=int, default=11730, help='Port to connect the Doremi server.')
http_parser.add_argument('--debug', action='store_true', help='Enable Debugging Output.')
http_parser.add_argument('--http-bind', default='0.0.0.0', help='HTTP bind address for serving API.')
http_parser.add_argument('--http-port', default=8087, help='HTTP Port for serving API.')
http_parser.set_defaults(func=http)

version_parser = parsers.add_parser('version', help="Display the version of the dcitools library.")
version_parser.set_defaults(func=version)

Expand Down
7 changes: 6 additions & 1 deletion dcitools/devices/doremi/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,14 @@ def construct_message(message, *args, **kwargs):
if element.name in kwargs.keys():
arg = kwargs[element.name]
else:
if arg_iterator >= len(args):
raise Exception('Parameter %s is missing' % element.name)
arg = args[arg_iterator]
arg_iterator += 1
value = element.func(arg, **element.kwargs)
try:
value = element.func(arg, **element.kwargs)
except Exception as e:
raise Exception("Error while parsing value of parameter '%s' : ERROR - %s" % (element.name, e))
payload_values.append(value)

payload = id + b''.join(payload_values)
Expand Down
127 changes: 127 additions & 0 deletions dcitools/devices/doremi/http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4 nu
"""
(c) 2014 Ronan Delacroix
Doremi DCP2000 CLI Only Utility - Main File
:author: Ronan Delacroix
"""
import sys
import os
import cmd
import shlex
import tbx.text
import logging
from . import server as server
from . import requests
import six
import bottle
from bottle import request


def routeapp(obj):
for kw in dir(obj):
attr = getattr(obj, kw)
if hasattr(attr, 'route'):
method = 'GET'
if hasattr(attr, 'method'):
method = attr.method
bottle.route(attr.route, method=method)(attr)


def methodroute(route, method=None):
def decorator(f):
f.route = route
if method:
f.method = method
return f
return decorator


class HTTPProxy(object):
def __init__(self, address, port, debug=False):
self.address = address
self.port = port
self.debug = debug

self.connect()

def connect(self):
print("Connection...")
try:
self.client = server.DoremiServer(self.address, port=self.port, debug=self.debug) #, bypass_connection=True)
except:
print("Connection to %s:%s failed." % (self.address, self.port))
exit(1)
print("Connected to Doremi DCP2000 server on %s:%s" % (self.address, self.port))


@methodroute('/')
def index(self):
return {
'available_commands': requests.list_names()
}

@methodroute('/<command>', method='GET')
def doc(self, command):
status = "error"
message = "Unknown error"

if command not in requests.list_names():
message = 'Unknown command name - "%s" not available' % command

req = requests.get(command)

key = req.key.encode('hex')

parameters = [{"name": e.name, "type": e.func.__name__.replace('_to_bytes', '')} for e in req.elements]

status = "success"
message = "OK"

return {
"command": command,
"key": key,
"parameters": parameters,
"status": status,
"message": message

}

@methodroute('/<command>', method='POST')
def request(self, command):
payload = dict(request.json)
status = "error"
message = "Unknown error"
result = {}

if command not in requests.list_names():
message = 'Unknown command name - "%s" not available' % command

result, success = self.call_api(command=command, kwargs=payload)

if success:
status = "success"
message = "OK"
else:
message = result
result = None

return {
"command": command,
"payload": payload,
"status": status,
"message": message,
"result": result
}

def call_api(self, command, kwargs):
"""
Call an API command
"""
try:
return (self.client.command(command, **kwargs), True)
except Exception as e:
logging.exception("Error while launching client.command")
print("ERROR : %s" % e)
return ("Error : %s" % e, False)
12 changes: 8 additions & 4 deletions dcitools/devices/doremi/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:author: Ronan Delacroix
"""
from . import commands
from . import requests
import tbx.network


Expand All @@ -19,16 +20,19 @@ class DoremiServer:
Handles sending and receiving commands through sockets.
"""

def __init__(self, host, port=11730, debug=False):
def __init__(self, host, port=11730, debug=False, bypass_connection=False):
"""
Create connection and connect to the server
"""
self.host = host
self.port = port
self.debug = debug

self.socket = tbx.network.SocketClient(host, port, timeout=TIMEOUT)
self.socket.connect()
if not bypass_connection:
self.socket = tbx.network.SocketClient(host, port, timeout=TIMEOUT)
self.socket.connect()
else:
self.socket = None

def command(self, key, *args, **kwargs):
"""
Expand All @@ -49,7 +53,7 @@ def __getattr__(self, key):
"""
Allows retrieval of callable command.
"""
if key in commands.NAMES:
if key in requests.list_names():
return commands.CommandCall(self.socket, key, self.debug, self.host, self.port)
else:
raise AttributeError
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
tbx >= 1.3.3
six >= 1.8.0
pysnmp >= 4.2.5
pysnmp >= 4.2.5
bottle

0 comments on commit 0108411

Please sign in to comment.