123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- #!/usr/bin/env python
- import os
- import io
- import sys
- import json
- import argparse
- import requests
- import ConfigParser
- import logging
- import xdg.BaseDirectory
- class Config(object):
- def __init__(self, dataset_id=None, root=None):
- self.root = root or os.path.abspath('.nova')
- self.name = None
- self.token = None
- self.remote = None
- self.id = dataset_id
- if os.path.exists(self.path):
- parser = ConfigParser.RawConfigParser()
- parser.read(self.path)
- def get(section, option):
- try:
- return parser.get(section, option)
- except ConfigParser.NoOptionError:
- return None
- self.token = get('core', 'token')
- self.remote = get('core', 'remote')
- self.id = get('core', 'id')
- def override(self, args, global_conf):
- args = vars(args)
- def get(arg):
- return args.get(arg, None)
- self.token = get('token') or global_conf.token or self.token
- self.remote = get('remote') or global_conf.remote or self.remote
- self.name = get('name') or self.name
- self.id = get('id') or self.id
- @property
- def path(self):
- return os.path.join(self.root, 'config')
- def write(self):
- parser = ConfigParser.RawConfigParser()
- parser.add_section('core')
- parser.set('core', 'remote', self.remote)
- parser.set('core', 'token', self.token)
- parser.set('core', 'id', self.id)
- if not os.path.exists(self.root):
- os.makedirs(self.root)
- with open(self.path, 'wb') as f:
- parser.write(f)
- def url(self, u):
- if not self.remote:
- logging.error("No remote specified in configuration or as an argument.")
- sys.exit(1)
- return '{}{}'.format(self.remote, u)
- @property
- def dataset_url(self):
- if not self.id:
- logging.error("No identifier specified.")
- return self.url('/api/datasets/{}'.format(self.id))
- def __repr__(self):
- return '<Config remote={}, token={}, id={}>'.format(self.remote, self.token, self.id)
- def push(config):
- from nova import memtar
- params = dict(token=config.token)
- f = memtar.create_tar(os.path.abspath('.'))
- f.seek(0L)
- url = config.remote + '/upload/' + config.id
- r = requests.post(url, params=params, data=f)
- if r.status_code == 200:
- return
- if r.status_code == 423:
- logging.error("Dataset is closed and cannot be modified.")
- else:
- logging.error(r.reason)
- def clone(config):
- from nova import memtar
- params = dict(token=config.token)
- if not config.name:
- r = requests.get(config.dataset_url, params=params)
- name = r.json()['name']
- else:
- name = config.name
- url = config.url('/clone/' + str(config.id))
- r = requests.get(url, params=params)
- f = io.BytesIO(r.content)
- memtar.extract_tar(f, os.path.join(os.path.abspath('.'), name))
- config.root = os.path.join(os.path.abspath(name), '.nova')
- config.write()
- def close(config):
- params = dict(token=config.token)
- data = dict(id=config.id, closed=True)
- r = requests.put(config.dataset_url, params=params, data=data)
- if r.status_code != 200:
- logging.error(r.reason)
- def init(config):
- name = config.name if config.name else os.path.basename(os.path.abspath(os.curdir))
- # create dataset on remote
- data = dict(name=name)
- params = dict(token=config.token)
- r = requests.post(config.url('/api/datasets'), params=params, data=data)
- result = r.json()
- # write initial configuration
- config.id = result['id']
- config.write()
- def info(config):
- params = dict(token=config.token)
- r = requests.get(config.url('/api/datasets/{}'.format(config.id)), params=params)
- print json.dumps(r.json(), indent=2)
- def list_datasets(config):
- params = dict(token=config.token)
- r = requests.get(config.url('/api/datasets'), params=params)
- data = r.json()
- if r.status_code != 200:
- print data['message']
- else:
- for d in data:
- print d['id'], d['name']
- if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- cmd_parsers = parser.add_subparsers(title="Commands")
- def add_remote_and_token_args(parser):
- parser.add_argument('--remote', type=str, help="URL of remote NOVA instance")
- parser.add_argument('--token', type=str, help="Access token")
- init_parser = cmd_parsers.add_parser('init', help="Initialize dataset in current directory")
- init_parser.add_argument('--name', type=str, help="Dataset name, if not given current directory name")
- init_parser.set_defaults(run=init)
- add_remote_and_token_args(init_parser)
- list_parser = cmd_parsers.add_parser('list', help="List datasets assigned to me")
- list_parser.set_defaults(run=list_datasets)
- add_remote_and_token_args(list_parser)
- push_parser = cmd_parsers.add_parser('push', help="Finalize data and push to remote")
- push_parser.set_defaults(run=push)
- add_remote_and_token_args(push_parser)
- clone_parser = cmd_parsers.add_parser('clone', help="Clone dataset")
- clone_parser.add_argument('--name', type=str, help="Alternative directory name")
- clone_parser.add_argument('id', type=int, help="Dataset identifier")
- clone_parser.set_defaults(run=clone)
- add_remote_and_token_args(clone_parser)
- close_parser = cmd_parsers.add_parser('close', help="Close dataset")
- close_parser.set_defaults(run=close)
- add_remote_and_token_args(close_parser)
- info_parser = cmd_parsers.add_parser('info', help="Gather information about dataset")
- info_parser.add_argument('id', type=int, help="Dataset identifier")
- info_parser.set_defaults(run=info)
- add_remote_and_token_args(info_parser)
- args = parser.parse_args()
- global_conf = Config(root=xdg.BaseDirectory.load_first_config('nova'))
- config = Config()
- config.override(args, global_conf);
- args.run(config)
|