MANIFEST.in
+config.py
setup.cfg
setup.py
ceph_brag/__init__.py
ceph_brag/app.py
+ceph_brag/json.py
ceph_brag.egg-info/PKG-INFO
ceph_brag.egg-info/SOURCES.txt
ceph_brag.egg-info/dependency_links.txt
ceph_brag/tests/config.py
ceph_brag/tests/test_functional.py
ceph_brag/tests/test_units.py
-public/css/style.css
-public/images/logo.png
\ No newline at end of file
from pecan import make_app
-from ceph_brag import model
+from ceph_brag import model, json
from pecan.hooks import TransactionHook
def setup_app(config):
from pecan import expose, request, abort, response
from webob.exc import status_map
from pecan.rest import RestController
-from ceph_brag.model.db import put_new_version
+from ceph_brag.model import db
import sys, traceback
class RootController(RestController):
- @expose()
+ @expose('json')
def get(self, *args, **kwargs):
- return ""
+ if len(args) is 0:
+ #return the list of uuids
+ try:
+ result = db.get_uuids()
+ except Exception as e:
+ traceback.print_exc()
+ abort(500, comment="Internal Server Error")
+ elif len(args) is 1 or len(args) is 2 and args[1] == '':
+ #/uuid
+ try:
+ result = db.get_versions(args[0])
+ except Exception as e:
+ traceback.print_exc()
+ abort(status_code=500, comment="Internal Server Error")
+
+ if result is None:
+ abort(400, comment="Invalid UUID")
+ elif len(args) is 2 or len(args) is 3 and args[2] == '':
+ #/uuid/version_number
+ try:
+ result = db.get_brag(args[0], args[1])
+ except Exception as e:
+ traceback.print_exc()
+ abort(status_code=500, comment="Internal Server Error")
- @expose()
+ if result is None:
+ abort(status_code=400, comment="Invalid UUID,version combination")
+ else:
+ abort(status_code=400, comment="Invalid args")
+
+ return result
+
+ @expose(content_type='application/json')
def put(self, *args, **kwargs):
try:
- put_new_version(request.body)
+ db.put_new_version(request.body)
except Exception as e:
- print >> sys.stderr, "Got an exception " + str(e)
- traceback.print_exc()
- abort(status_code=500, comment="Internal Server Error")
-
- print >> sys.stderr, "Successfully completed the new version"
- response.status_int = 201
- return "Created"
+ traceback.print_exc()
+ abort(status_code=500, comment="Internal Server Error")
+
+ response.status = 201
+ return "CREATED"
@expose()
- def delete(self):
- return ""
+ def delete(self, *args, **kwargs):
+ uuid = kwargs['uuid']
+ if uuid is None:
+ abort(status_code=400, comment="Required uuid parameter")
+
+ try:
+ status = db.delete_uuid(uuid)
+ except Exception as e:
+ traceback.print_exc()
+ abort(status_code=500, comment="Internal Server Error")
+
+ if status is not None:
+ abort(status_code=status['status'], comment=status['comment'])
+
+ response.status=200
+ return "DELETED"
--- /dev/null
+from pecan.jsonify import jsonify
+from ceph_brag.model import db
+
+@jsonify.register(db.version_info)
+def jsonify_version(vi):
+ return dict(
+ version_id=vi.index,
+ version_number=vi.version_number,
+ version_date=str(vi.version_date)
+ )
+
+@jsonify.register(db.cluster_info)
+def jsonify_cluster_info(ci):
+ return dict(
+ uuid=ci.uuid,
+ organization=ci.organization,
+ email=ci.contact_email,
+ cluster_name=ci.cluster_name,
+ cluster_creation_date=str(ci.cluster_creation_date),
+ )
+
+@jsonify.register(db.components_info)
+def jsonify_components_info(comps):
+ return dict(
+ bytes={'count':comps.byte_count, 'scale':comps.byte_scale},
+ osds=comps.num_osds,
+ objects=comps.num_objects,
+ pgs=comps.num_pgs,
+ pools=comps.num_pools,
+ mdss=comps.num_mdss,
+ mons=comps.num_mons
+ )
+
+@jsonify.register(db.pools_info)
+def jsonify_pools_info(pool):
+ return dict(rep_size=pool.pool_rep_size,
+ name=pool.pool_name,
+ id=pool.pool_id
+ )
+
+@jsonify.register(db.osds_info)
+def jsonify_osds_info(osd):
+ return dict(nw_info={'address':osd.nw_address,'hostname':osd.hostname},
+ hw_info={'swap_kb':osd.swap_kb,'mem_kb':osd.mem_kb,
+ 'arch':osd.arch, 'cpu':osd.cpu},
+ id=osd.osd_id,
+ os_info={'os':osd.os,'version':osd.os_version,
+ 'description':osd.os_desc, 'distro':osd.distro},
+ ceph_version=osd.ceph_version
+ )
+
+@jsonify.register(db.brag)
+def jsonify_brag(b):
+ ownership = {'organization':b.ci.organization,
+ 'description':b.ci.description,
+ 'email':b.ci.contact_email,
+ 'name':b.ci.cluster_name
+ }
+ crush_types=b.comps.crush_types.split(',')
+ return dict(uuid=b.ci.uuid,
+ cluster_creation_date=str(b.ci.cluster_creation_date),
+ components_count=b.comps,
+ crush_types=crush_types,
+ ownership=ownership,
+ pool_metadata=b.pools,
+ sysinfo=b.osds
+ )
Base.metadata.create_all(engine)
def start():
- print >> sys.stderr, " --- MODEL START"
Session.bind = conf.sqlalchemy.engine
def commit():
- print >> sys.stderr, " --- MODEL COMMIT"
Session.commit()
def rollback():
- print >> sys.stderr, " --- MODEL ROLLBACK"
Session.rollback()
def clear():
- print >> sys.stderr, " --- MODEL CLEAR"
Session.remove()
contact_email = Column(String)
cluster_name = Column(String)
cluster_creation_date = Column(DateTime)
+ description = Column(String)
num_versions = Column(Integer)
class version_info(Base):
distro = Column(String)
ceph_version = Column(String)
+class brag(object):
+ def __init__(self, uuid, version_number):
+ self.ci = Session.query(cluster_info).filter_by(uuid=uuid).first()
+ if self.ci is not None:
+ self.vi = Session.query(version_info).filter_by(cluster_id=self.ci.index, version_number=version_number).first()
+
+ if self.ci is not None and self.vi is not None:
+ self.comps = Session.query(components_info).filter_by(vid=self.vi.index).first()
+ self.pools = Session.query(pools_info).filter_by(vid=self.vi.index).all()
+ self.osds = Session.query(osds_info).filter_by(vid=self.vi.index).all()
+
def put_new_version(data):
info = json.loads(data)
def add_cluster_info():
organization=info['ownership']['organization'],
contact_email=info['ownership']['email'],
cluster_name=info['ownership']['name'],
+ description=info['ownership']['description'],
cluster_creation_date=dt,
num_versions=1)
Session.add(ci)
add_components_info(vi)
add_pools_info(vi)
add_osds_info(vi)
-
+
+def delete_uuid(uuid):
+ ci = Session.query(cluster_info).filter_by(uuid=uuid).first()
+ if ci is None:
+ return {'status':400, 'comment':'No information for this UUID'}
+
+ for v in Session.query(version_info).filter_by(cluster_id=ci.index).all():
+ Session.query(components_info).filter_by(vid=v.index).delete()
+ Session.query(pools_info).filter_by(vid=v.index).delete()
+ Session.query(osds_info).filter_by(vid=v.index).delete()
+ Session.delete(v)
+
+ Session.delete(ci)
+ return None
+
+def get_uuids():
+ return Session.query(cluster_info).all()
+
+def get_versions(uuid):
+ ci = Session.query(cluster_info).filter_by(uuid=uuid).first()
+ if ci is None:
+ return None
+
+ return Session.query(version_info).filter_by(cluster_id=ci.index).all()
+
+def get_brag(uuid, version_id):
+ b = brag(uuid, version_id)
+ if b.ci is None or b.vi is None:
+ return None
+
+ return b
from webtest import TestApp
from ceph_brag.tests import FunctionalTest
import sys
+from pecan import request
class TestRootController(FunctionalTest):
def test_get(self):
response = self.app.get('/?k1=v1&k2=v2')
- print >> sys.stderr, "Vars of response = " + str(vars(response))
assert response.status_int == 200
- def test_search(self):
- response = self.app.post('/', params={'q': 'RestController'})
+ def test_put(self):
+ #response = self.app.put('/', upload_files=[("uploadfield", "/tmp/sample.json")])
+ with open ("/tmp/sample.json", "r") as myfile:
+ data=myfile.read().replace('\n', '')
+ response = self.app.request('/', method='PUT', body=data)
assert response.status_int == 302
- assert response.headers['Location'] == (
- 'http://pecan.readthedocs.org/en/latest/search.html'
- '?q=RestController'
- )
def test_get_not_found(self):
response = self.app.get('/a/bogus/url', expect_errors=True)
sqlalchemy = {
'url' : 'sqlite:////tmp/test.db',
- 'echo' : True,
+ 'echo' : False,
'encoding' : 'utf-8'
}
+++ /dev/null
-body {
- background: #311F00;
- color: white;
- font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif;
- padding: 1em 2em;
-}
-
-a {
- color: #FAFF78;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-div#content {
- width: 800px;
- margin: 0 auto;
-}
-
-form {
- margin: 0;
- padding: 0;
- border: 0;
-}
-
-fieldset {
- border: 0;
-}
-
-input.error {
- background: #FAFF78;
-}
-
-header {
- text-align: center;
-}
-
-h1, h2, h3, h4, h5, h6 {
- font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif;
- text-transform: uppercase;
-}