]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Server changes to deploy in a production env
authorBabu Shanmugam <anbu@enovance.com>
Mon, 10 Feb 2014 07:08:26 +0000 (12:38 +0530)
committerBabu Shanmugam <anbu@enovance.com>
Mon, 10 Feb 2014 07:08:26 +0000 (12:38 +0530)
1. Added the wsgi entry point app.wsgi
2. Updated client code to mandate the update-metadata to have url to publish and unpublish
3. Updated the README to describe a bit about the server operations as well.

README.md
ceph-brag [deleted file]
client/ceph-brag [new file with mode: 0755]
server/app.wsgi [new file with mode: 0644]

index 26de521d6b061691d9cf2fc3051203c42cfb31b7..37468163b752e765021d7d9443fd95e94af5e4f0 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,18 +8,17 @@ For more information please visit:
 * [Blueprint](http://wiki.ceph.com/Planning/Blueprints/Firefly/Ceph-Brag)
 * [CDS Etherpad](http://pad.ceph.com/p/cdsfirefly-ceph-brag)
 
-How to use:
-===========
-Pre-requisites:
----------------
+# Client
+
+## How to use:
+
+### Pre-requisites:
 ceph-brag uses 'ceph' python script. Hence, before executing ceph-brag script ensure that ceph services are all running and 'ceph' script is in 'PATH' environment
 
-Runtime instructions:
----------------------
+### Runtime instructions:
 Run 'ceph-brag -h' to get the usage information of this tool.
 
-Sample output:
---------------
+### Sample output:
 
     {
       "cluster_creation_date": "2014-01-16 13:38:41.928551",
@@ -95,3 +94,66 @@ Sample output:
         }
       ]
     }
+
+
+# Server
+
+## Info
+The ceph-brag server code is a python based web application. 
+
+## How to use
+
+### Prerequisites
+* [pecan](http://pecanpy.org) is the web framework that is used by this application.
+
+### How to deploy
+* [Common recipes to deploy](http://pecan.readthedocs.org/en/latest/deployment.html#common-recipes)
+
+## URLs
+Following are the REST urls that are implemented with 'url-prefix' being the mount point for the WSGI script
+
+### GET
+
+##### * GET /url-prefix/
+Returns the list of clusters that are registered so far. 
+Outputs - On success application/json of the following format is returned
+
+    [
+      {
+       "num_versions": 3, 
+       "cluster_creation_date": "2014-01-16 13:38:41.928551", 
+       "uuid": "20679d0e-04b1-4004-8ee9-45ac271510e9", 
+       "cluster_name": "Cluster1", 
+       "organization": "eNovance", 
+       "email": "mail@enovance.com"
+      },
+      ...
+    ]
+
+##### * GET /url-prefix/UUID
+Returns the list of version information for a particular UUID.
+Outputs - On success application/json of the following format is returned
+
+    [
+      {
+        "version_number": 1, 
+        "version_date": "2014-02-10 10:17:56.283499", 
+        "version_id": 10
+      },
+      ...
+    ]
+
+##### * GET /url-prefix/UUID/version\_number
+Returns the entire brag report as mentioned in client's sample output for a particular version of a UUID
+
+### PUT
+
+##### * PUT /url-prefix
+Uploads the brag report and creates a new version for the UUID mentioned in the payload
+
+### DELETE
+
+##### * DELETE /url-prefix?uuid=xxxx
+Deletes all the versions of a cluster whose UUID is sent as a parameter
+
+
diff --git a/ceph-brag b/ceph-brag
deleted file mode 100755 (executable)
index f37b618..0000000
--- a/ceph-brag
+++ /dev/null
@@ -1,306 +0,0 @@
-#!/usr/bin/env python
-
-import subprocess
-import uuid
-import re
-import json
-import sys
-import ast
-
-CLUSTER_UUID_NAME='cluster-uuid'
-CLUSTER_OWNERSHIP_NAME='cluster-ownership'
-
-def run_command(cmd):
-  child = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                       stderr=subprocess.PIPE)
-  (o, e) = child.communicate()
-  return (child.returncode, o, e)
-
-def get_uuid():
-  (rc,uid,e) = run_command(['ceph', 'config-key', 'get', CLUSTER_UUID_NAME])
-  if rc is not 0:
-    #uuid is not yet set.
-    uid = str(uuid.uuid4())
-    (rc, o, e) = run_command(['ceph', 'config-key', 'put',
-                             CLUSTER_UUID_NAME, uid])
-    if rc is not 0:
-      raise RuntimeError("\'ceph config-key put\' failed -" + e)
-
-  return uid
-
-def get_cluster_creation_date():
-  (rc, o, e) = run_command(['ceph', 'mon', 'dump'])
-  if rc is not 0:
-    raise RuntimeError("\'ceph mon dump\' failed - " + e)
-
-  rec = re.compile('(.*created\ )(.*)(\n.*)')
-
-  mo = rec.search(o);
-  if mo and mo.group(2) != '0.000000':
-    return mo.group(2)
-
-  # Try and get the date from osd dump
-  (rc, o, e) = run_command(['ceph', 'osd', 'dump'])
-  if rc is not 0:
-    raise RuntimeError("\'ceph osd dump\' failed - " + e)
-
-  mo = rec.search(o);
-  if not mo or mo.group(2) == '0.000000':
-    print >> sys.stderr, "Unable to get cluster creation date"
-
-  return mo.group(2)
-
-def get_nums():
-  (rc, o, e) = run_command(['ceph', '-s'])
-  if rc is not 0:
-    raise RuntimeError("\'ceph -s\' failed - " + e)
-
-  num_mons = 0
-
-  mo = re.search('(.*monmap\ .*:\ )(\d+)(.*)', o)
-  if not mo:
-    raise RuntimeError("Unmatched pattern for monmap in \'ceph status\'")
-  else:
-    num_mons = int(mo.group(2))
-
-  num_osds = 0
-  mo = re.search('.*osdmap.*(\d+).*(\d+).*(\d+).*', o)
-  if not mo:
-    raise RuntimeError("Unmatched pattern for osdmap in \'ceph status\'")
-  else:
-    num_osds = int(mo.group(1))
-
-  num_mdss = 0
-  mo = re.search('.*mdsmap\ e\d+.*(\d+)/(\d+)/(\d+).*', o)
-  if mo:
-    num_mdss = int(mo.group(2));
-
-  num_pgs = 0
-  num_pools = 0
-  num_bytes = 0
-  num_objs = 0
-  mo = re.search('.*pgmap\ v\d+:\ (\d+).*,\ (\d+).*,\ (\d+)\ (\S+)\ data,\ (\d+).*', o)
-  if not mo:
-    raise RuntimeError("Unmatched pattern for pgmap in \'ceph status\'")
-  else:
-    num_pgs = int(mo.group(1))
-    num_pools = int(mo.group(2))
-    byte_count = int(mo.group(3))
-    byte_scale = mo.group(4)
-    num_objs = int(mo.group(5))
-  nums = {'mons':num_mons,
-          'osds':num_osds,
-          'mdss':num_mdss,
-          'pgs':num_pgs,
-          'pools':num_pools,
-          'bytes': {'count':byte_count, 'scale':byte_scale},
-          'objects':num_objs}
-  return nums
-
-def get_crush_types():
-  (rc, o, e) = run_command(['ceph', 'osd', 'crush', 'dump'])
-  if rc is not 0:
-    raise RuntimeError("\'ceph osd crush dump\' failed - " + e)
-
-  crush_dump = json.loads(o)
-  if crush_dump['types'] is None:
-    raise RuntimeError("\'types\' item missing in \'ceph osd crush dump\'")
-
-  crush_types = []
-  for t in crush_dump['types']:
-    crush_types.append(t['name'])
-
-  return crush_types
-
-def get_pool_metadata():
-  (rc, o, e) = run_command(['ceph', 'osd', 'dump'])
-  if rc is not 0:
-    raise RuntimeError("\'ceph osd dump\' failed - " + e)
-
-  result = re.findall("pool\ (\d+)\ '(\S+)'\ rep\ size\ (\d+)", o)
-  if len(result) is 0:
-    #Check with replicated size
-    result = re.findall("pool\ (\d+)\ '(\S+)'\ replicated\ size\ (\d+)", o)
-    if len(result) is 0:
-      raise RuntimeError("Unmatched pattern for \'pool\' in \'ceph osd dump\'")
-
-  pool_meta = []
-  proc = lambda x: {'id':x[0], 'name':x[1], 'rep_size':int(x[2])}
-  for r in result:
-    pool_meta.append(proc(r))
-
-  return pool_meta
-
-def get_sysinfo(max_osds):
-  sysinfo = []
-  count = 0
-  osd_metadata_available = False
-
-  while count < max_osds:
-    meta = {'id':count}
-    (rc, o, e) = run_command(['ceph', 'osd', 'metadata', str(count)])
-    if rc is 0:
-      if osd_metadata_available is False:
-        osd_metadata_available = True
-      os_info = {}
-      hw_info = {}
-      nw_info = {}
-
-      jmeta = json.loads(o)
-
-      meta['ceph_version'] = jmeta['ceph_version']
-
-      os_info['os'] = jmeta['os']
-      os_info['version'] = jmeta['kernel_version']
-      os_info['description'] = jmeta['kernel_description']
-
-      try:
-        distro = jmeta['distro'] + ' '
-        distro += jmeta['distro_version'] + ' '
-        distro += jmeta['distro_codename'] + ' ('
-        distro += jmeta['distro_description'] + ')'
-        os_info['distro'] = distro
-      except KeyError as ke:
-        pass
-      meta['os_info'] = os_info
-  
-      hw_info['cpu'] = jmeta['cpu']
-      hw_info['arch'] = jmeta['arch']
-      hw_info['mem_kb'] = int(jmeta['mem_total_kb'])
-      hw_info['swap_kb'] = int(jmeta['mem_swap_kb'])
-      meta['hw_info'] = hw_info
-  
-    (ip, hname) = get_osd_host(count)
-    nw_info['address'] = ip
-    nw_info['hostname'] = hname
-    meta['nw_info'] = nw_info
-
-    sysinfo.append(meta)
-    count = count + 1
-
-  if osd_metadata_available is False:
-    print >> sys.stderr, "'ceph osd metadata' is not available at all"
-
-  return sysinfo
-
-def get_osd_host(osd_id):
-  loc = {}
-
-  (rc, o, e) = run_command(['ceph', 'osd', 'find', str(osd_id)])
-  if rc is not 0:
-    raise RuntimeError("\'ceph osd find\' failed - " + e)
-
-  jloc = json.loads(o)
-
-  mo = re.search("(\d+.\d+.\d+.\d+).*", jloc['ip'])
-  if mo is None:
-    #Might be in ipv6 format, TODO: Verify
-    return None;
-
-  ip = mo.group(1)
-  host = jloc['crush_location']['host']
-
-  return (ip, host)
-
-def get_ownership_info():
-  (rc, o, e) = run_command(['ceph', 'config-key', 'get',
-                            CLUSTER_OWNERSHIP_NAME])
-  if rc is not 0:
-    return {}
-
-  return ast.literal_eval(o)
-
-def output_json():
-  out = {}
-  out['uuid'] = get_uuid()
-  out['cluster_creation_date'] = get_cluster_creation_date()
-  nums = get_nums()
-  num_osds = int(nums['osds'])
-  out['components_count'] = nums
-  out['crush_types'] = get_crush_types()
-  out['pool_metadata'] = get_pool_metadata()
-  out['sysinfo'] = get_sysinfo(num_osds)
-
-  owner = get_ownership_info()
-  if owner is not None:
-    out['ownership'] = owner
-
-  return json.dumps(out, indent=2, separators=(',', ': '))
-
-def describe_usage():
-  print >> sys.stderr, "Usage:"
-  print >> sys.stderr, "======\n"
-
-  print >> sys.stderr, sys.argv[0] + " <commands> [command-options]\n"
-  print >> sys.stderr, "commands:"
-  print >> sys.stderr, "publish - publish the brag report to the server"
-  print >> sys.stderr, "update-metadata <update-metadata-options> - Update"
-  print >> sys.stderr, "         ownership information for bragging"
-  print >> sys.stderr, "clear-metadata - Clear information set by update-metadata"
-  print >> sys.stderr, "unpublish --yes-i-am-shy - delete the brag report from the server"
-  print >> sys.stderr, ""
-
-  print >> sys.stderr, "update-metadata options:"
-  print >> sys.stderr, "--name=  - Name of the cluster"
-  print >> sys.stderr, "--organization= - Name of the organization"
-  print >> sys.stderr, "--email= - Email contact address"
-  print >> sys.stderr, "--description= - Reporting use-case"
-  print >> sys.stderr, ""
-
-def update_metadata():
-  info = {}
-  possibles = ['name', 'organization', 'email', 'description']
-
-  #get the existing values
-  info = get_ownership_info();
-
-  for index in range(2, len(sys.argv)):
-    mo = re.search("--(\S+)=(.*)", sys.argv[index])
-    if not mo:
-      describe_usage()
-      return 22
-
-    k = mo.group(1)
-    v = mo.group(2)
-
-    if k in possibles:
-      info[k] = v
-    else:
-      print >> sys.stderr, "Unexpect option --" + k
-      describe_usage()
-      return 22
-
-  (rc, o, e) = run_command(['ceph', 'config-key', 'put',
-                            CLUSTER_OWNERSHIP_NAME, str(info)])
-  return rc
-
-def clear_metadata():
-  (rc, o, e) = run_command(['ceph', 'config-key', 'del',
-                            CLUSTER_OWNERSHIP_NAME])
-  return rc
-
-def main():
-  if len(sys.argv) is 1:
-    print output_json()
-    return 0
-  elif sys.argv[1] == 'update-metadata':
-    return update_metadata()
-  elif sys.argv[1] == 'clear-metadata':
-    return clear_metadata()
-  elif sys.argv[1] == 'publish':
-    data = output_json()
-    #TODO: post this data to the server
-    return 0
-  elif sys.argv[1] == 'unpublish':
-    if len(sys.argv) <= 2 or sys.argv[2] != '--yes-i-am-shy':
-      print >> sys.stderr, "unpublish should be followed by --yes-i-am-shy"
-      return 22
-
-    #TODO: delete this data from the server
-    return 0
-  else:
-    describe_usage()
-    return 22
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/client/ceph-brag b/client/ceph-brag
new file mode 100755 (executable)
index 0000000..e7398a8
--- /dev/null
@@ -0,0 +1,335 @@
+#!/usr/bin/env python
+
+import subprocess
+import uuid
+import re
+import json
+import sys
+import ast
+
+CLUSTER_UUID_NAME='cluster-uuid'
+CLUSTER_OWNERSHIP_NAME='cluster-ownership'
+
+def run_command(cmd):
+  child = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                       stderr=subprocess.PIPE)
+  (o, e) = child.communicate()
+  return (child.returncode, o, e)
+
+def get_uuid():
+  (rc,uid,e) = run_command(['ceph', 'config-key', 'get', CLUSTER_UUID_NAME])
+  if rc is not 0:
+    #uuid is not yet set.
+    uid = str(uuid.uuid4())
+    (rc, o, e) = run_command(['ceph', 'config-key', 'put',
+                             CLUSTER_UUID_NAME, uid])
+    if rc is not 0:
+      raise RuntimeError("\'ceph config-key put\' failed -" + e)
+
+  return uid
+
+def get_cluster_creation_date():
+  (rc, o, e) = run_command(['ceph', 'mon', 'dump'])
+  if rc is not 0:
+    raise RuntimeError("\'ceph mon dump\' failed - " + e)
+
+  rec = re.compile('(.*created\ )(.*)(\n.*)')
+
+  mo = rec.search(o);
+  if mo and mo.group(2) != '0.000000':
+    return mo.group(2)
+
+  # Try and get the date from osd dump
+  (rc, o, e) = run_command(['ceph', 'osd', 'dump'])
+  if rc is not 0:
+    raise RuntimeError("\'ceph osd dump\' failed - " + e)
+
+  mo = rec.search(o);
+  if not mo or mo.group(2) == '0.000000':
+    print >> sys.stderr, "Unable to get cluster creation date"
+
+  return mo.group(2)
+
+def get_nums():
+  (rc, o, e) = run_command(['ceph', '-s'])
+  if rc is not 0:
+    raise RuntimeError("\'ceph -s\' failed - " + e)
+
+  num_mons = 0
+
+  mo = re.search('(.*monmap\ .*:\ )(\d+)(.*)', o)
+  if not mo:
+    raise RuntimeError("Unmatched pattern for monmap in \'ceph status\'")
+  else:
+    num_mons = int(mo.group(2))
+
+  num_osds = 0
+  mo = re.search('.*osdmap.*(\d+).*(\d+).*(\d+).*', o)
+  if not mo:
+    raise RuntimeError("Unmatched pattern for osdmap in \'ceph status\'")
+  else:
+    num_osds = int(mo.group(1))
+
+  num_mdss = 0
+  mo = re.search('.*mdsmap\ e\d+.*(\d+)/(\d+)/(\d+).*', o)
+  if mo:
+    num_mdss = int(mo.group(2));
+
+  num_pgs = 0
+  num_pools = 0
+  num_bytes = 0
+  num_objs = 0
+  mo = re.search('.*pgmap\ v\d+:\ (\d+).*,\ (\d+).*,\ (\d+)\ (\S+)\ data,\ (\d+).*', o)
+  if not mo:
+    raise RuntimeError("Unmatched pattern for pgmap in \'ceph status\'")
+  else:
+    num_pgs = int(mo.group(1))
+    num_pools = int(mo.group(2))
+    byte_count = int(mo.group(3))
+    byte_scale = mo.group(4)
+    num_objs = int(mo.group(5))
+  nums = {'mons':num_mons,
+          'osds':num_osds,
+          'mdss':num_mdss,
+          'pgs':num_pgs,
+          'pools':num_pools,
+          'bytes': {'count':byte_count, 'scale':byte_scale},
+          'objects':num_objs}
+  return nums
+
+def get_crush_types():
+  (rc, o, e) = run_command(['ceph', 'osd', 'crush', 'dump'])
+  if rc is not 0:
+    raise RuntimeError("\'ceph osd crush dump\' failed - " + e)
+
+  crush_dump = json.loads(o)
+  if crush_dump['types'] is None:
+    raise RuntimeError("\'types\' item missing in \'ceph osd crush dump\'")
+
+  crush_types = []
+  for t in crush_dump['types']:
+    crush_types.append(t['name'])
+
+  return crush_types
+
+def get_pool_metadata():
+  (rc, o, e) = run_command(['ceph', 'osd', 'dump'])
+  if rc is not 0:
+    raise RuntimeError("\'ceph osd dump\' failed - " + e)
+
+  result = re.findall("pool\ (\d+)\ '(\S+)'\ rep\ size\ (\d+)", o)
+  if len(result) is 0:
+    #Check with replicated size
+    result = re.findall("pool\ (\d+)\ '(\S+)'\ replicated\ size\ (\d+)", o)
+    if len(result) is 0:
+      raise RuntimeError("Unmatched pattern for \'pool\' in \'ceph osd dump\'")
+
+  pool_meta = []
+  proc = lambda x: {'id':x[0], 'name':x[1], 'rep_size':int(x[2])}
+  for r in result:
+    pool_meta.append(proc(r))
+
+  return pool_meta
+
+def get_sysinfo(max_osds):
+  sysinfo = []
+  count = 0
+  osd_metadata_available = False
+
+  while count < max_osds:
+    meta = {'id':count}
+    (rc, o, e) = run_command(['ceph', 'osd', 'metadata', str(count)])
+    if rc is 0:
+      if osd_metadata_available is False:
+        osd_metadata_available = True
+      os_info = {}
+      hw_info = {}
+      nw_info = {}
+
+      jmeta = json.loads(o)
+
+      meta['ceph_version'] = jmeta['ceph_version']
+
+      os_info['os'] = jmeta['os']
+      os_info['version'] = jmeta['kernel_version']
+      os_info['description'] = jmeta['kernel_description']
+
+      try:
+        distro = jmeta['distro'] + ' '
+        distro += jmeta['distro_version'] + ' '
+        distro += jmeta['distro_codename'] + ' ('
+        distro += jmeta['distro_description'] + ')'
+        os_info['distro'] = distro
+      except KeyError as ke:
+        pass
+      meta['os_info'] = os_info
+  
+      hw_info['cpu'] = jmeta['cpu']
+      hw_info['arch'] = jmeta['arch']
+      hw_info['mem_kb'] = int(jmeta['mem_total_kb'])
+      hw_info['swap_kb'] = int(jmeta['mem_swap_kb'])
+      meta['hw_info'] = hw_info
+  
+    (ip, hname) = get_osd_host(count)
+    nw_info['address'] = ip
+    nw_info['hostname'] = hname
+    meta['nw_info'] = nw_info
+
+    sysinfo.append(meta)
+    count = count + 1
+
+  if osd_metadata_available is False:
+    print >> sys.stderr, "'ceph osd metadata' is not available at all"
+
+  return sysinfo
+
+def get_osd_host(osd_id):
+  loc = {}
+
+  (rc, o, e) = run_command(['ceph', 'osd', 'find', str(osd_id)])
+  if rc is not 0:
+    raise RuntimeError("\'ceph osd find\' failed - " + e)
+
+  jloc = json.loads(o)
+
+  mo = re.search("(\d+.\d+.\d+.\d+).*", jloc['ip'])
+  if mo is None:
+    #Might be in ipv6 format, TODO: Verify
+    return None;
+
+  ip = mo.group(1)
+  host = jloc['crush_location']['host']
+
+  return (ip, host)
+
+def get_ownership_info():
+  (rc, o, e) = run_command(['ceph', 'config-key', 'get',
+                            CLUSTER_OWNERSHIP_NAME])
+  if rc is not 0:
+    return {}
+
+  return ast.literal_eval(o)
+
+def output_json():
+  out = {}
+  url = None
+  
+  out['uuid'] = get_uuid()
+  out['cluster_creation_date'] = get_cluster_creation_date()
+  nums = get_nums()
+  num_osds = int(nums['osds'])
+  out['components_count'] = nums
+  out['crush_types'] = get_crush_types()
+  out['pool_metadata'] = get_pool_metadata()
+  out['sysinfo'] = get_sysinfo(num_osds)
+
+  owner = get_ownership_info()
+  if owner is not None:
+    url = owner.pop['url']
+    out['ownership'] = owner
+
+  return json.dumps(out, indent=2, separators=(',', ': ')), url
+
+def describe_usage():
+  print >> sys.stderr, "Usage:"
+  print >> sys.stderr, "======\n"
+
+  print >> sys.stderr, sys.argv[0] + " <commands> [command-options]\n"
+  print >> sys.stderr, "commands:"
+  print >> sys.stderr, "publish - publish the brag report to the server"
+  print >> sys.stderr, "update-metadata <update-metadata-options> - Update"
+  print >> sys.stderr, "         ownership information for bragging"
+  print >> sys.stderr, "clear-metadata - Clear information set by update-metadata"
+  print >> sys.stderr, "unpublish --yes-i-am-shy - delete the brag report from the server"
+  print >> sys.stderr, ""
+
+  print >> sys.stderr, "update-metadata options:"
+  print >> sys.stderr, "--name=  - Name of the cluster"
+  print >> sys.stderr, "--organization= - Name of the organization"
+  print >> sys.stderr, "--email= - Email contact address"
+  print >> sys.stderr, "--description= - Reporting use-case"
+  print >> sys.stderr, "--url= - The URL that is used to publish and unpublish"
+  print >> sys.stderr, ""
+
+def update_metadata():
+  info = {}
+  possibles = ['name', 'organization', 'email', 'description', 'url']
+
+  #get the existing values
+  info = get_ownership_info();
+
+  for index in range(2, len(sys.argv)):
+    mo = re.search("--(\S+)=(.*)", sys.argv[index])
+    if not mo:
+      describe_usage()
+      return 22
+
+    k = mo.group(1)
+    v = mo.group(2)
+
+    if k in possibles:
+      info[k] = v
+    else:
+      print >> sys.stderr, "Unexpect option --" + k
+      describe_usage()
+      return 22
+
+  (rc, o, e) = run_command(['ceph', 'config-key', 'put',
+                            CLUSTER_OWNERSHIP_NAME, str(info)])
+  return rc
+
+def clear_metadata():
+  (rc, o, e) = run_command(['ceph', 'config-key', 'del',
+                            CLUSTER_OWNERSHIP_NAME])
+  return rc
+
+def publish():
+  data, url = output_json()
+  if url is None:
+    print >> sys.stderr, "Cannot publish until a URL is set using update-metadata"
+    return 1
+
+  #TODO: Execute the HTTP request
+  return 0
+
+def unpublish():
+  if len(sys.argv) <= 2 or sys.argv[2] != '--yes-i-am-shy':
+    print >> sys.stderr, "unpublish should be followed by --yes-i-am-shy"
+    return 22
+
+  fail = False
+  owner = get_ownership_info()
+  if owner is None:
+    fail = True
+  try:
+    url = owner['url']
+  except KeyError as e:
+    fail = True
+
+  if fail:
+    print >> sys.stderr, "URL is not updated yet"
+    return 1
+
+  uuid = get_uuid()
+
+  #TODO: Execute the HTTP request
+  return 0
+
+def main():
+  if len(sys.argv) is 1:
+    print output_json()[0]
+    return 0
+  elif sys.argv[1] == 'update-metadata':
+    return update_metadata()
+  elif sys.argv[1] == 'clear-metadata':
+    return clear_metadata()
+  elif sys.argv[1] == 'publish':
+    return publish()
+  elif sys.argv[1] == 'unpublish':
+    return unpublish()
+  else:
+    describe_usage()
+    return 22
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/server/app.wsgi b/server/app.wsgi
new file mode 100644 (file)
index 0000000..01dfc72
--- /dev/null
@@ -0,0 +1,5 @@
+import os
+from pecan.deploy import deploy
+
+cur_path = os.path.dirname(__file__)
+application = deploy(cur_path + '/config.py')