nova 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #!/usr/bin/env python
  2. import os
  3. import io
  4. import sys
  5. import json
  6. import argparse
  7. import requests
  8. import ConfigParser
  9. import logging
  10. import xdg.BaseDirectory
  11. class Config(object):
  12. def __init__(self, dataset_id=None, root=None):
  13. self.root = root or os.path.abspath('.nova')
  14. self.name = None
  15. self.token = None
  16. self.remote = None
  17. self.id = dataset_id
  18. if os.path.exists(self.path):
  19. parser = ConfigParser.RawConfigParser()
  20. parser.read(self.path)
  21. def get(section, option):
  22. try:
  23. return parser.get(section, option)
  24. except ConfigParser.NoOptionError:
  25. return None
  26. self.token = get('core', 'token')
  27. self.remote = get('core', 'remote')
  28. self.id = get('core', 'id')
  29. def override(self, args, global_conf):
  30. args = vars(args)
  31. def get(arg):
  32. return args.get(arg, None)
  33. self.token = get('token') or global_conf.token or self.token
  34. self.remote = get('remote') or global_conf.remote or self.remote
  35. self.name = get('name') or self.name
  36. self.id = get('id') or self.id
  37. @property
  38. def path(self):
  39. return os.path.join(self.root, 'config')
  40. def write(self):
  41. parser = ConfigParser.RawConfigParser()
  42. parser.add_section('core')
  43. parser.set('core', 'remote', self.remote)
  44. parser.set('core', 'token', self.token)
  45. parser.set('core', 'id', self.id)
  46. if not os.path.exists(self.root):
  47. os.makedirs(self.root)
  48. with open(self.path, 'wb') as f:
  49. parser.write(f)
  50. def url(self, u):
  51. if not self.remote:
  52. logging.error("No remote specified in configuration or as an argument.")
  53. sys.exit(1)
  54. return '{}{}'.format(self.remote, u)
  55. @property
  56. def dataset_url(self):
  57. if not self.id:
  58. logging.error("No identifier specified.")
  59. return self.url('/api/datasets/{}'.format(self.id))
  60. def __repr__(self):
  61. return '<Config remote={}, token={}, id={}>'.format(self.remote, self.token, self.id)
  62. def push(config):
  63. from nova import memtar
  64. params = dict(token=config.token)
  65. f = memtar.create_tar(os.path.abspath('.'))
  66. f.seek(0L)
  67. url = config.remote + '/upload/' + config.id
  68. r = requests.post(url, params=params, data=f)
  69. if r.status_code == 200:
  70. return
  71. if r.status_code == 423:
  72. logging.error("Dataset is closed and cannot be modified.")
  73. else:
  74. logging.error(r.reason)
  75. def clone(config):
  76. from nova import memtar
  77. params = dict(token=config.token)
  78. if not config.name:
  79. r = requests.get(config.dataset_url, params=params)
  80. name = r.json()['name']
  81. else:
  82. name = config.name
  83. url = config.url('/clone/' + str(config.id))
  84. r = requests.get(url, params=params)
  85. f = io.BytesIO(r.content)
  86. memtar.extract_tar(f, os.path.join(os.path.abspath('.'), name))
  87. config.root = os.path.join(os.path.abspath(name), '.nova')
  88. config.write()
  89. def close(config):
  90. params = dict(token=config.token)
  91. data = dict(id=config.id, closed=True)
  92. r = requests.put(config.dataset_url, params=params, data=data)
  93. if r.status_code != 200:
  94. logging.error(r.reason)
  95. def init(config):
  96. name = config.name if config.name else os.path.basename(os.path.abspath(os.curdir))
  97. # create dataset on remote
  98. data = dict(name=name)
  99. params = dict(token=config.token)
  100. r = requests.post(config.url('/api/datasets'), params=params, data=data)
  101. result = r.json()
  102. # write initial configuration
  103. config.id = result['id']
  104. config.write()
  105. def info(config):
  106. params = dict(token=config.token)
  107. r = requests.get(config.url('/api/datasets/{}'.format(config.id)), params=params)
  108. print json.dumps(r.json(), indent=2)
  109. def list_datasets(config):
  110. params = dict(token=config.token)
  111. r = requests.get(config.url('/api/datasets'), params=params)
  112. data = r.json()
  113. if r.status_code != 200:
  114. print data['message']
  115. else:
  116. for d in data:
  117. print d['id'], d['name']
  118. if __name__ == '__main__':
  119. parser = argparse.ArgumentParser()
  120. cmd_parsers = parser.add_subparsers(title="Commands")
  121. def add_remote_and_token_args(parser):
  122. parser.add_argument('--remote', type=str, help="URL of remote NOVA instance")
  123. parser.add_argument('--token', type=str, help="Access token")
  124. init_parser = cmd_parsers.add_parser('init', help="Initialize dataset in current directory")
  125. init_parser.add_argument('--name', type=str, help="Dataset name, if not given current directory name")
  126. init_parser.set_defaults(run=init)
  127. add_remote_and_token_args(init_parser)
  128. list_parser = cmd_parsers.add_parser('list', help="List datasets assigned to me")
  129. list_parser.set_defaults(run=list_datasets)
  130. add_remote_and_token_args(list_parser)
  131. push_parser = cmd_parsers.add_parser('push', help="Finalize data and push to remote")
  132. push_parser.set_defaults(run=push)
  133. add_remote_and_token_args(push_parser)
  134. clone_parser = cmd_parsers.add_parser('clone', help="Clone dataset")
  135. clone_parser.add_argument('--name', type=str, help="Alternative directory name")
  136. clone_parser.add_argument('id', type=int, help="Dataset identifier")
  137. clone_parser.set_defaults(run=clone)
  138. add_remote_and_token_args(clone_parser)
  139. close_parser = cmd_parsers.add_parser('close', help="Close dataset")
  140. close_parser.set_defaults(run=close)
  141. add_remote_and_token_args(close_parser)
  142. info_parser = cmd_parsers.add_parser('info', help="Gather information about dataset")
  143. info_parser.add_argument('id', type=int, help="Dataset identifier")
  144. info_parser.set_defaults(run=info)
  145. add_remote_and_token_args(info_parser)
  146. args = parser.parse_args()
  147. global_conf = Config(root=xdg.BaseDirectory.load_first_config('nova'))
  148. config = Config()
  149. config.override(args, global_conf);
  150. args.run(config)