Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

refactoring and updates

  • Loading branch information...
commit 39192eb8eecfa46c7ef89157b8e0568795354ea9 1 parent 262a373
@AndreCharbonneau AndreCharbonneau authored
View
5 Makefile
@@ -12,3 +12,8 @@ deploy-remote:
ssh root@$(RPI_WEB_HOST) "mkdir -p $(RPI_WEB_WSGI)"
rsync -avz -e ssh --delete --exclude '.git' --exclude '.rope*' --exclude '*~' --delete-excluded wsgi/ root@$(RPI_WEB_HOST):$(RPI_WEB_WSGI)
ssh root@$(RPI_WEB_HOST) "service httpd reload"
+
+deploy-cs-infoserver:
+ ssh andrec@condor.heprc.uvic.ca "mkdir -p ~/bin"
+ scp xmlrpc/rpiinfoservers/cloudscheduler_infoserver.py andrec@condor.heprc.uvic.ca:bin/
+
View
35 wsgi/conf/rpi-api.conf.sample
@@ -0,0 +1,35 @@
+[CloudSchedulerService]
+url_base=cloudscheduler
+xmlrpc_server=http://condor.heprc.uvic.ca:8111
+name=Cloud Scheduler
+synopsis=A service to schedule work between clouds.
+institution=UVIC HEP Group
+version=?
+# release_time must be a timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
+release_time=?
+invocations=0
+# last_reset must be a timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
+last_reset=?
+documentation=?
+release_notes=?
+support=?
+source=<a href="https://github.com/hep-gc/cloud-scheduler">https://github.com/hep-gc/cloud-scheduler</a>
+#tryme=
+
+
+[CvmfsService]
+url_base=cvmfs
+name=CVMFS
+synopsis=CVMFS is a scalable read-only HTTP file system designed for distributed software deployment that was developed at the CERN Laboratory.
+institution=UVIC HEP Group
+version=?
+# release_time must be a timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
+release_time=?
+invocations=0
+# last_reset must be a timestamp in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
+last_reset=?
+documentation=?
+release_notes=?
+support=?
+#source=
+#tryme=
View
10 wsgi/rpi_web.py
@@ -27,20 +27,14 @@
try:
logging.debug('Creating request dispatching routes...')
d = cherrypy.dispatch.RoutesDispatcher()
- m = d.mapper
-
conf['/'] = {'request.dispatch': d}
-
# Create the handlers.
- cloudSchedulerService = CloudSchedulerService()
- cvmfsService = CvmfsService()
+ cloudSchedulerService = CloudSchedulerService(d)
+ cvmfsService = CvmfsService(d)
- # Connect the handler's routes.
- cloudSchedulerService.connectRoutes(d)
- cvmfsService.connectRoutes(d)
View
127 wsgi/services/base.py
@@ -1,14 +1,61 @@
+import os
import cherrypy
import datetime
import json
import logging
+import ConfigParser
from utils import HtmlUtils
+
+
+
class RpiService():
"""
An abstract base class for all services.
+
+ This base class will implement the required RPI REST API as
+ specified by CANARIE. Where it needs to needs to fetch dynamic
+ data from the services, it will do so via a web service protocol
+ (i.e., xmlrpm), and then format the data accouring to the RPI API
+ specs and return the results to the caller. This abstraction
+ layer means that the services themselves do not need to know
+ anything about the RPI API specs and can return the data using any
+ formatting they want.
+
+ This class also implements a configuration framework, allowing
+ each child of this class to have a configuration section in the
+ app configuration file.
"""
+ def __init__(self, d):
+ # Load configuration file
+ self._config = ConfigParser.ConfigParser()
+ configFile = os.path.expanduser('/etc/canarie-rpi/rpi-api.cfg')
+ if os.path.exists(configFile):
+ self._config.readfp(open(configFile))
+ else:
+ logging.info('%s configuration file does not exist. Using defaults.' % (configFile))
+
+ self._connectRoutes(d)
+
+
+ def _getConfigSection(self):
+ return self.__class__.__name__
+
+ def _getFromConfig(self, option, default = None):
+ if self._config.has_option(self._getConfigSection(), option):
+ return self._config.get(self._getConfigSection(), option)
+ else:
+ return default
+
+ def _parseISOTimestamp(self, timestamp):
+ if timestamp == None:
+ return None
+ else:
+ # convert the ISO8601 string to a datetime object
+ converted = datetime.datetime.strptime(timestamp, "%Y%m%dT%H:%M:%S")
+ return converted
+
#
# URL routes handlers.
# The following route handlers will be called when specific URLs are visited.
@@ -183,14 +230,36 @@ def tryme(self):
- def connectRoutes(self, d):
- d.connect(self.__class__.__name__ + '-info', '/%s/service/info' % (self.getUrlBase()), controller = self, action = 'info')
- d.connect(self.__class__.__name__ + '-stats', '/%s/service/stats' % (self.getUrlBase()), controller = self, action = 'stats')
- d.connect(self.__class__.__name__ + '-doc', '/%s/service/doc' % (self.getUrlBase()), controller = self, action = 'doc')
- d.connect(self.__class__.__name__ + '-releasenotes', '/%s/service/releasenotes' % (self.getUrlBase()), controller = self, action = 'releasenotes')
- d.connect(self.__class__.__name__ + '-support', '/%s/service/support' % (self.getUrlBase()), controller = self, action = 'support')
- d.connect(self.__class__.__name__ + '-source', '/%s/service/source' % (self.getUrlBase()), controller = self, action = 'source')
- d.connect(self.__class__.__name__ + '-tryme', '/%s/service/tryme' % (self.getUrlBase()), controller = self, action = 'tryme')
+ def _connectRoutes(self, d):
+ """
+ Call this method to register the URL routes.
+ This method will register the following URIs:
+
+ <base>/service/info
+ <base>/service/stats
+ <base>/service/doc
+ <base>/service/releasenotes
+ <base>/service/support
+ <base>/service/source
+ <base>/service/tryme
+
+ """
+ d.connect(self.__class__.__name__ + '-info', '/%s/service/info' % (self._getUrlBase()), controller = self, action = 'info')
+ d.connect(self.__class__.__name__ + '-stats', '/%s/service/stats' % (self._getUrlBase()), controller = self, action = 'stats')
+ d.connect(self.__class__.__name__ + '-doc', '/%s/service/doc' % (self._getUrlBase()), controller = self, action = 'doc')
+ d.connect(self.__class__.__name__ + '-releasenotes', '/%s/service/releasenotes' % (self._getUrlBase()), controller = self, action = 'releasenotes')
+ d.connect(self.__class__.__name__ + '-support', '/%s/service/support' % (self._getUrlBase()), controller = self, action = 'support')
+ d.connect(self.__class__.__name__ + '-source', '/%s/service/source' % (self._getUrlBase()), controller = self, action = 'source')
+ d.connect(self.__class__.__name__ + '-tryme', '/%s/service/tryme' % (self._getUrlBase()), controller = self, action = 'tryme')
+
+
+ def _getUrlBase(self):
+ return self._getFromConfig('url_base', self.getName().lower())
+
+
+ def _getXmlRpcServer(self):
+ return self._getFromConfig('xmlrpc_server')
+
@@ -200,58 +269,62 @@ def connectRoutes(self, d):
#
- # The following abastract methods needs to be implemented in
+ # The following abstract methods can be overriden in
# each RPI service subclasses.
# These are defined in the "RPI API Enhancements for CANARIE
# Service Registry and Monitoring System" document.
+ # The default implementation will try to read the value from
+ # the /etc/canarie-rpi/rpi-api.cfg configuration file.
#
- def getUrlBase(self):
- raise NotImplementedError()
-
def getName(self):
- return self.__class__.__name__
+ return self._getFromConfig('name', self.__class__.__name__)
def getSynopsis(self):
- raise NotImplementedError()
+ return self._getFromConfig('synopsis')
def getVersion(self):
- raise NotImplementedError()
+ return self._getFromConfig('version')
def getInstitution(self):
- raise NotImplementedError()
+ return self._getFromConfig('institution')
def getReleaseTime(self):
- """
- Must return a datetime.datetime object.
- """
- raise NotImplementedError()
+ return self._parseISOTimestamp(self._getFromConfig('release_time'))
def getInvocations(self):
- raise NotImplementedError()
+ return self._getFromConfig('invocations')
def getLastReset(self):
"""
Must return a datetime.datetime object.
"""
- raise NotImplementedError()
+ return self._parseISOTimestamp(self._getFromConfig('last_reset'))
def getDoc(self):
- raise NotImplementedError()
+ return self._getFromConfig('documentation')
def getReleaseNotes(self):
- raise NotImplementedError()
+ return self._getFromConfig('release_notes')
def getSupport(self):
- raise NotImplementedError()
+ return self._getFromConfig('support')
def getSource(self):
# Defaults to 'No Content' if not implemented in subclass.
- cherrypy.response.status = 204
+ source = self._getFromConfig('support')
+ if source == None:
+ cherrypy.response.status = 204
+ else:
+ return source
def getTryMe(self):
# Defaults to 'No Content' if not implemented in subclass.
- cherrypy.response.status = 204
+ tryme = self._getFromConfig('tryme')
+ if tryme == None:
+ cherrypy.response.status = 204
+ else:
+ return tryme
View
27 wsgi/services/cloudscheduler.py
@@ -1,18 +1,11 @@
from base import RpiService
import datetime
+import xmlrpclib
class CloudSchedulerService(RpiService):
- def getUrlBase(self):
- return 'cloudscheduler'
-
- def getSynopsis(self):
- return 'A service to schedule work between clouds.'
-
- def getVersion(self):
- return '0.1'
-
- def getInstitution(self):
- return 'UVIC HEP Group'
+# def getVersion(self):
+# proxy = xmlrpclib.ServerProxy(self._getXmlRpcServer())
+# return proxy.get_version()
def getReleaseTime(self):
"""
@@ -26,15 +19,3 @@ def getInvocations(self):
def getLastReset(self):
return datetime.datetime.now()
- def getDoc(self):
- return 'This is the Cloud Scheuler service documentation page.'
-
- def getReleaseNotes(self):
- return 'These are the Cloud Scheuler service release notes.'
-
- def getSupport(self):
- return 'These are the Cloud Scheuler service support notes.'
-
- def getSource(self):
- return '<a href="https://github.com/hep-gc/cloud-scheduler">https://github.com/hep-gc/cloud-scheduler</a>'
-
View
35 wsgi/services/cvmfs.py
@@ -1,37 +1,4 @@
from base import RpiService
-import datetime
class CvmfsService(RpiService):
- def getUrlBase(self):
- return 'cvmfs'
-
- def getSynopsis(self):
- return 'CVMFS is a scalable read-only HTTP file system designed for distributed software deployment that was developed at the CERN Laboratory.'
-
- def getVersion(self):
- return '???'
-
- def getInstitution(self):
- return 'UVIC HEP Group'
-
- def getReleaseTime(self):
- """
- Must return a datetime.datetime object.
- """
- return datetime.datetime.now()
-
- def getInvocations(self):
- return 0
-
- def getLastReset(self):
- return datetime.datetime.now()
-
- def getDoc(self):
- return 'This is the CVMFS service documentation page.'
-
- def getReleaseNotes(self):
- return 'These are the CVMFS service release notes.'
-
- def getSupport(self):
- return 'These are the CVMFS service support notes.'
-
+ pass
View
61 xmlrpc/rpiinfoservers/cloudscheduler_infoserver.py
@@ -0,0 +1,61 @@
+import logging
+import datetime
+from SimpleXMLRPCServer import SimpleXMLRPCServer
+from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
+
+import cloudscheduler.__version__ as version
+
+
+
+# Restrict to a particular path.
+class RequestHandler(SimpleXMLRPCRequestHandler):
+ rpc_paths = ('/RPC2',)
+
+# Create server
+server = SimpleXMLRPCServer(("condor.heprc.uvic.ca", 8000),
+ requestHandler=RequestHandler)
+server.register_introspection_functions()
+
+
+# Register an instance; all the methods of the instance are
+# published as XML-RPC methods (in this case, just 'div').
+class ExternalFunctions():
+ def ping(self):
+ """
+ Simple test method to check if the info server is up.
+ Might sound redundant, but makes client code more explicit.
+ """
+ return 'OK'
+
+ def get_version(self):
+ """
+ Returns the version of the currently installed Cloud Scheduler libraries.
+ """
+ return version.version
+
+ def get_release_time(self):
+ """
+ Returns the release time of the currently installed Cloud Scheduler libraries,
+ in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm or, if microsecond is 0, YYYY-MM-DDTHH:MM:SS).
+ """
+ return '%s' % datetime.datetime.now().isoformat()
+
+ def get_number_of_invocations(self):
+ """
+ Returns the number of time the Cloud Scheduler server was 'invoked', along with the timestamp
+ at which this number was last reset.
+ We define a Cloud Scheduler invocation as being the Cloud Scheduler accepting a new job submission.
+ This method
+ The format of the returned value is:
+ <count>;<timestamp>
+ The timestamp is in UTC and formatted in ISO 8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm or, if microsecond is 0, YYYY-MM-DDTHH:MM:SS)
+ """
+ return '0;%s' % (datetime.datetime.now().isoformat())
+
+
+
+server.register_instance(ExternalFunctions())
+
+# Run the server's main loop
+logging.debug('Starting xmlrpc server loop ...')
+server.serve_forever()
Please sign in to comment.
Something went wrong with that request. Please try again.