From 759dc7c8392d983eb8d966ef333b8f42a89d812c Mon Sep 17 00:00:00 2001 From: Dan Radez Date: Fri, 20 Apr 2018 10:43:39 -0400 Subject: [PATCH] Adding schedule and interface handling for host - also added rude testing script that has been used to verify so far. --- bin/quads-cli | 410 ++++++++++++++++++++++-------------------------- dev_tests.sh | 79 ++++++++++ quads/api_v2.py | 102 +++++++++--- quads/model.py | 50 +++++- 4 files changed, 393 insertions(+), 248 deletions(-) create mode 100644 dev_tests.sh diff --git a/bin/quads-cli b/bin/quads-cli index 4c889eab4..e3dc66af0 100755 --- a/bin/quads-cli +++ b/bin/quads-cli @@ -19,30 +19,6 @@ logger.addHandler(ch) API = 'v2' -apiurl = {'v2': { -'lshosts': 'host', -'lsclouds': 'cloud', -'lsowner': 'owner', -'lsccusers': 'ccuser', -'lsticket': 'ticket', -'lsqinq': 'qinq', -'lswipe': 'wipe', -'hostresource': 'hostresource', -'cloudresource': 'cloudresource', -'addschedule': 'schedule', -'rmhost': 'host', -'rmcloud': 'cloud', -'rmschedule': 'schedule', -'modschedule': 'schedule', -'movehosts': 'host', -'host': 'host', -'cloudonly': 'cloud', -'datearg': 'date', -'summary': 'summary', -'fullsummary': 'summary', -'lsschedule': 'schedule',}} - - # used to load the configuration for quads behavior def quads_load_config(quads_config): try: @@ -50,36 +26,55 @@ def quads_load_config(quads_config): try: quads_config_yaml = yaml.safe_load(config_file) except Exception, ex: - print "quads: Invalid YAML config: " + quads_config + print 'quads: Invalid YAML config: ' + quads_config exit(1) except Exception, ex: print ex exit(1) return(quads_config_yaml) +def output_json_result(request, item, data, debug=False): + try: + js = request.json() + if debug: + print '%s %s: %s' % (request.status_code, request.reason, data) + else: + for result in js['result']: + print result + except: + print 'Could not parse json reply.' + if debug: + print request.text + else: + print 'use --debug to see contents' + exit(1) + def main(argv): - quads_config_file = os.path.dirname(__file__) + "/../conf/quads.yml" + quads_config_file = os.path.dirname(__file__) + '/../conf/quads.yml' quads_config = quads_load_config(quads_config_file) - if "data_dir" not in quads_config: - print "quads: Missing \"data_dir\" in " + quads_config_file + if 'data_dir' not in quads_config: + print 'quads: Missing \'data_dir\' in ' + quads_config_file exit(1) - if "install_dir" not in quads_config: - print "quads: Missing \"install_dir\" in " + quads_config_file + if 'install_dir' not in quads_config: + print 'quads: Missing \'install_dir\' in ' + quads_config_file exit(1) - if "quads_base_url" not in quads_config: - print "quads: Missing \"quads_base_url\" in " + quads_config_file + if 'quads_base_url' not in quads_config: + print 'quads: Missing \'quads_base_url\' in ' + quads_config_file exit(1) + else: + api_url = os.path.join(quads_config['quads_base_url'], 'api', API) - sys.path.append(quads_config["install_dir"] + "/lib") - sys.path.append(os.path.dirname(__file__) + "/../lib") + sys.path.append(quads_config['install_dir'] + '/lib') + + sys.path.append(os.path.dirname(__file__) + '/../lib') from Quads import Quads - defaultconfig = quads_config["data_dir"] + "/schedule.yaml" - defaultstatedir = quads_config["data_dir"] + "/state" - defaultmovecommand = "/bin/echo" + defaultconfig = quads_config['data_dir'] + '/schedule.yaml' + defaultstatedir = quads_config['data_dir'] + '/state' + defaultmovecommand = '/bin/echo' parser = argparse.ArgumentParser(description='Query current cloud for a given host') group = parser.add_mutually_exclusive_group() @@ -92,7 +87,9 @@ def main(argv): group.add_argument('--define-cloud', dest='cloudresource', type=str, default=None, help='Define a cloud environment') group.add_argument('--add-schedule', dest='addschedule', action='store_true', help='Define a host reservation') group.add_argument('--mod-schedule', dest='modschedule', type=int, default=None, help='Modify a host reservation') + group.add_argument('--add-interface', dest='addinterface', type=str, default=None, help='Define a host interface') group.add_argument('--rm-schedule', dest='rmschedule', type=int, default=None, help='Remove a host reservation') + group.add_argument('--rm-interface', dest='rminterface', type=str, default=None, help='Remove a host interface') group.add_argument('--ls-hosts', dest='action', action='store_const', const='host', help='List all hosts') group.add_argument('--ls-clouds', dest='action', action='store_const', const='cloud', help='List all clouds') group.add_argument('--rm-host', dest='rmhost', type=str, default=None, help='Remove a host') @@ -100,7 +97,7 @@ def main(argv): parser.add_argument('--host', dest='host', type=str, default=None, help='Specify the host to query') parser.add_argument('--cloud-only', dest='cloudonly', type=str, default=None, help='Limit full report to hosts only in this cloud') parser.add_argument('-c', '--config', dest='config', help='YAML file with cluster data', default=defaultconfig, type=str) - parser.add_argument('-d', '--datetime', dest='datearg', type=str, default=None, help='date and time to query; e.g. "2016-06-01 08:00"') + parser.add_argument('-d', '--datetime', dest='datearg', type=str, default=None, help='date and time to query; e.g. \'2016-06-01 08:00\'') parser.add_argument('--cloud-owner', dest='cloudowner', type=str, default=None, help='Define environment owner') parser.add_argument('--cc-users', dest='ccusers', type=str, default=None, help='Define environment CC list') parser.add_argument('--qinq', dest='qinq', type=str, default=None, help='Define environment qinq state') @@ -117,7 +114,11 @@ def main(argv): parser.add_argument('--schedule-start', dest='schedstart', type=str, default=None, help='Schedule start date/time') parser.add_argument('--schedule-end', dest='schedend', type=str, default=None, help='Schedule end date/time') parser.add_argument('--schedule-cloud', dest='schedcloud', type=str, default=None, help='Schedule cloud') - parser.add_argument('--ls-schedule', dest='lsschedule', action='store_true', help='List the host reservations') + parser.add_argument('--ls-schedule', dest='action', action='store_const', const='schedule', help='List the host reservations') + parser.add_argument('--interface-mac', dest='ifmac', type=str, default=None, help='') + parser.add_argument('--interface-type', dest='iftype', type=str, default=None, help='') + parser.add_argument('--interface-port', dest='ifport', type=str, default=None, help='') + parser.add_argument('--ls-interface', dest='action', action='store_const', const='interfaces', help='List the host interfaces') parser.add_argument('--statedir', dest='statedir', type=str, default=defaultstatedir, help='Default state dir') parser.add_argument('--move-hosts', dest='movehosts', action='store_true', default=None, help='Move hosts if schedule has changed') parser.add_argument('--move-command', dest='movecommand', type=str, default=defaultmovecommand, help='External command to move a host') @@ -132,242 +133,209 @@ def main(argv): parser.add_argument('--debug', action='store_true', default=False, help='Show debugging information.') args = parser.parse_args() + # List all common call if args.action: - url = os.path.join(quads_config["quads_base_url"], "api", API, args.action) + url = os.path.join(api_url, args.action) if args.cloudonly: url += '?cloudonly=%s' % args.cloudonly + if args.host: + url += '?host=%s' % args.host if args.debug: print(url) r = requests.get(url) - data = r.json() - if args.debug: - print(data) - for k in data: - print k[args.action] - exit(0) + if r.ok: + data = r.json() + if args.debug: + print(data) + for k in data: + print k[args.action] + exit(0) + else: + # TODO: is this host or generic? + print '%s host %s %s' % (r.status_code, args.rmhost, r.reason) + + # Cloud Add / Modify + elif args.cloudresource: + url = os.path.join(api_url, 'cloud') + data = {'cloud': args.cloudresource, + 'description': args.description, + 'owner': args.cloudowner, + 'cc': args.ccusers, + 'qinq': args.qinq, + 'wipe': args.wipe, + 'ticket': args.cloudticket, + 'force': args.force } + r = requests.post(url, data) + output_json_result(r, 'cloud', data) - if args.hostresource: + # Cloud Remove + elif args.rmcloud is not None: + url = os.path.join(api_url, 'cloud', args.rmcloud) + r = requests.delete(url) + output_json_result(r, 'cloud', {'cloud': args.rmcloud}) + + # Host Add / Modify + elif args.hostresource: if args.cloudresource: - print "--define-cloud and --define-host are mutually exclusive." + print '--define-cloud and --define-host are mutually exclusive.' exit(1) - url = quads_config["quads_base_url"] + "api/" + API + "/host" - data = { "host": args.hostresource, "cloud": args.hostcloud, "type": args.hosttype, "force": args.force } + url = os.path.join(api_url, 'host') + data = {'host': args.hostresource, + 'cloud': args.hostcloud, + 'type': args.hosttype, + 'force': args.force } r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - exit(0) - - if args.cloudresource: - url = quads_config["quads_base_url"] + "api/" + API + "/cloud" - data = { "cloud": args.cloudresource, - "description": args.description, - "owner": args.cloudowner, - "cc": args.ccusers, - "qinq": args.qinq, - "wipe": args.wipe, - "ticket": args.cloudticket, - "force": args.force } - r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - exit(0) + output_json_result(r, 'host', data) - if args.addschedule: - if args.schedstart is None or args.schedend is None or args.schedcloud is None or args.host is None: - print "Missing option. All these options are required for --add-schedule:" - print " --host" - print " --schedule-start" - print " --schedule-end" - print " --schedule-cloud" - url = quads_config["quads_base_url"] + "api/" + API + "/ahs" - data = { "cloud": args.schedcloud, - "host": args.host, - "start": args.schedstart, - "end": args.schedend } + # Host Remove + elif args.rmhost is not None: + url = os.path.join(api_url, 'host', args.rmhost) + r = requests.delete(url) + output_json_result(r, 'host', {'host': args.rmhost}) + + # Add Interface + elif args.addinterface: + if args.ifmac is None or args.iftype is None \ + or args.ifport is None or args.host is None: + print 'Missing option. All these options are required for --add-interface:' + print ' --host' + print ' --interface-mac' + print ' --interface-type' + print ' --interface-port' + url = os.path.join(api_url, 'interfaces') + data = {'mac': args.ifmac, + 'vendor_type': args.iftype, + 'host': args.host, + 'port': args.ifport, + 'interface': args.addinterface } r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - exit(0) + print data + output_json_result(r, 'interfaces', {'interfaces': 'cli-TODO'}) - if args.rmhost is not None: - url = os.path.join(quads_config["quads_base_url"], "api", API, 'host', args.rmhost) + elif args.rminterface is not None: + url = os.path.join(api_url, 'interfaces', str(args.rminterface), args.host) r = requests.delete(url) - if r.ok: - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - else: - print "%s host %s %s" % (r.status_code, args.rmhost, r.reason) - exit(0) + output_json_result(r, 'interfaces', {'interface': 'cli-TODO'}, debug=args.debug) + + # Add Schedule + elif args.addschedule: + if args.schedstart is None or args.schedend is None \ + or args.schedcloud is None or args.host is None: + print 'Missing option. All these options are required for --add-schedule:' + print ' --host' + print ' --schedule-start' + print ' --schedule-end' + print ' --schedule-cloud' + url = os.path.join(api_url, 'schedule') + data = {'cloud': args.schedcloud, + 'host': args.host, + 'start': args.schedstart, + 'end': args.schedend } + r = requests.post(url, data) + print data + output_json_result(r, 'schedule', {'schedule': 'cli-TODO'}) - if args.rmcloud is not None: - url = os.path.join(quads_config["quads_base_url"], "api", API, 'cloud', args.rmcloud) + elif args.rmschedule is not None: + url = os.path.join(api_url, 'schedule', str(args.rmschedule), args.host) r = requests.delete(url) - if r.ok: - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - else: - print "%s cloud %s %s" % (r.status_code, args.rmcloud, r.reason) - exit(0) + output_json_result(r, 'schedule', {'schedule': 'cli-TODO'}, debug=args.debug) - if args.rmschedule is not None: - url = quads_config["quads_base_url"] + "api/" + API + "/rhs" - data = { "schedule": args.rmschedule, - "host": args.host } - r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - exit(0) - - if args.modschedule is not None: + elif args.modschedule is not None: if args.host is None: - print "Missing option. Need --host when using --mod-schedule" + print 'Missing option. Need --host when using --mod-schedule' exit(1) if args.schedstart is None and args.schedend is None and args.schedcloud is None: - print "Missing option. At least one these options are required for --mod-schedule:" - print " --schedule-start" - print " --schedule-end" - print " --schedule-cloud" + print 'Missing option. At least one these options are required for --mod-schedule:' + print ' --schedule-start' + print ' --schedule-end' + print ' --schedule-cloud' exit(1) - url = quads_config["quads_base_url"] + "api/" + API + "/mhs" - data = { "schedule": args.modschedule, - "start": args.schedstart, - "end": args.schedend, - "cloud": args.schedcloud, - "host": args.host } + url = os.path.join(api_url, 'mhs') + data = {'schedule': args.modschedule, + 'start': args.schedstart, + 'end': args.schedend, + 'cloud': args.schedcloud, + 'host': args.host } r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - print "Unexpected error" - exit(1) - for result in js['result']: - print result - exit(0) - print "No result received" - exit(0) + output_json_result(r, 'schedule', data['schedule']) - if args.movehosts: + elif args.movehosts: if args.datearg is not None and not args.dryrun: - print "--move-hosts and --date are mutually exclusive unless using --dry-run." + print '--move-hosts and --date are mutually exclusive unless using --dry-run.' exit(1) - url = quads_config["quads_base_url"] + "api/" + API + "/moves" - data = {"statedir":args.statedir} + url = os.path.join(api_url, 'moves') + data = {'statedir':args.statedir} if args.datearg is not None: - data["date"] = args.datearg + data['date'] = args.datearg r = requests.post(url, data) js = r.json() if 'result' in js: if len(js['result']) == 0: - print "Nothing to do." + print 'Nothing to do.' exit(0) for result in js['result']: - host = result["host"] - current = result["current"] - new = result["new"] + host = result['host'] + current = result['current'] + new = result['new'] # now we need to know if we should wipe - url = quads_config["quads_base_url"] + "api/" + API + "/lswipe" - data = { "cloudonly": new } + url = os.path.join(api_url, 'lswipe') + data = { 'cloudonly': new } r = requests.post(url, data) wipe_js = r.json() wipe_value = '1' if 'wipe' in wipe_js: if len(wipe_js['wipe']) == 0: - print "ERROR" + print 'ERROR' exit(1) for wipe in wipe_js['wipe']: wipe_value = wipe[new] - print "Moving " + host + " from " + current + " to " + new + ", wipe = " + wipe_value + print 'Moving ' + host + ' from ' + current + ' to ' + new + ', wipe = ' + wipe_value if not args.dryrun: try: if wipe_value == '1': subprocess.check_call([args.movecommand, host, current, new]) else: - subprocess.check_call([args.movecommand, host, current, new, "nowipe"]) + subprocess.check_call([args.movecommand, host, current, new, 'nowipe']) except Exception, ex: - logger.error("Move command failed: %s" % ex) + logger.error('Move command failed: %s' % ex) exit(1) - stream = open(args.statedir + "/" + host, 'w') + stream = open(args.statedir + '/' + host, 'w') stream.write(new + '\n') stream.close() exit(0) - print "No result received" + print 'No result received' exit(0) - - data = {} - if args.host: - data['host'] = args.host - - if args.cloudonly: - data['cloud'] = args.cloudonly - - if args.datearg: - data['date'] = args.datearg - - if args.summary: - data['summary'] = args.summary - - if args.fullsummary: - data['fullsummary'] = args.fullsummary - - if args.lsschedule: - data['lsschedule'] = args.lsschedule - - url = quads_config["quads_base_url"] + "api/" + API + "/query" - r = requests.post(url, data) - js = r.json() - if 'result' in js: - if len(js['result']) == 0: - exit(0) - for result in js['result']: - print result - exit(0) - exit(0) - -if __name__ == "__main__": + # TODO + #data = {} + #if args.host: + # data['host'] = args.host + + #if args.cloudonly: + # data['cloud'] = args.cloudonly + + #if args.datearg: + # data['date'] = args.datearg + + #if args.summary: + # data['summary'] = args.summary + + #if args.fullsummary: + # data['fullsummary'] = args.fullsummary + + #url = os.path.join(api_url, 'query') + #r = requests.post(url, data) + #js = r.json() + #if 'result' in js: + # if len(js['result']) == 0: + # exit(0) + # for result in js['result']: + # print result + # exit(0) + #exit(0) + +if __name__ == '__main__': main(sys.argv[1:]) diff --git a/dev_tests.sh b/dev_tests.sh new file mode 100644 index 000000000..791a26601 --- /dev/null +++ b/dev_tests.sh @@ -0,0 +1,79 @@ +echo '------- define 3 clouds ---------------------' +bin/quads-cli --define-cloud cloud01 --description cloud01 +bin/quads-cli --define-cloud cloud02 --description cloud02 +bin/quads-cli --define-cloud cloud03 --description cloud03 + +echo '------- redefine cloud01 w/o force ----------' +bin/quads-cli --define-cloud cloud01 --description cloud01 +echo '------- redefine cloud01 w/force ------------' +bin/quads-cli --define-cloud cloud01 --description cloud01 --force + +echo '------- define hosts ------------------------' +bin/quads-cli --define-host host01.example.com --default-cloud cloud01 --host-type vendor +bin/quads-cli --define-host host02.example.com --default-cloud cloud01 --host-type vendor + +echo '------- redefine host01 w/o force ----------' +bin/quads-cli --define-host host01.example.com --default-cloud cloud01 --host-type vendor +echo '------- redefine host01 w/force ------------' +bin/quads-cli --define-host host01.example.com --default-cloud cloud01 --host-type vendor --force + + +echo '------- list owners -------------------------' +bin/quads-cli --ls-owner +echo '------- list tickets ------------------------' +bin/quads-cli --ls-ticket +echo '------- list qinq ---------------------------' +bin/quads-cli --ls-qinq +echo '------- list wipe ---------------------------' +bin/quads-cli --ls-wipe +echo '------- list ccusers ------------------------' +bin/quads-cli --ls-cc-user +echo '------- list owners w/cloudonly cloud01 -----' +bin/quads-cli --ls-owner --cloud-only cloud01 +echo '------- list tickets w/cloudonly cloud01 ----' +bin/quads-cli --ls-ticket --cloud-only cloud01 +echo '------- list qinq w/cloudonly cloud01 ------' +bin/quads-cli --ls-qinq --cloud-only cloud01 +echo '------- list wipe w/cloudonly cloud01 -------' +bin/quads-cli --ls-wipe --cloud-only cloud01 +echo '------- list ccusers w/cloudonly cloud01 ----' +bin/quads-cli --ls-cc-user --cloud-only cloud01 + + +echo '------- Add schedules' +bin/quads-cli --add-schedule --host host01.example.com --schedule-start "2018-04-27 08:00" --schedule-end "2018-08-19 08:00" --schedule-cloud cloud01 +bin/quads-cli --add-schedule --host host01.example.com --schedule-start "2019-04-27 08:00" --schedule-end "2019-08-19 08:00" --schedule-cloud cloud02 +bin/quads-cli --add-schedule --host host01.example.com --schedule-start "2016-04-27 08:00" --schedule-end "2016-08-19 08:00" --schedule-cloud cloud03 + +echo '------- List schedules' +bin/quads-cli --ls-schedule --host host01.example.com + +echo '------- delete schedules' +bin/quads-cli --rm-schedule 1 --host host01.example.com + +echo '------- List schedules' +bin/quads-cli --ls-schedule --host host01.example.com + +echo '------- delete schedules' +bin/quads-cli --rm-schedule 0 --host host01.example.com +bin/quads-cli --rm-schedule 0 --host host01.example.com + +echo '------- Add interfaces' +bin/quads-cli --add-interface em1 --interface-mac 00:00:00:00:00:00 --interface-type test --interface-port xe-0/0/0 --host host01.example.com + +echo '------- List interfaces' +bin/quads-cli --ls-interface --host host01.example.com + +echo '------- delete interfaces' +bin/quads-cli --rm-interface em1 --host host01.example.com + +echo '------- remove hosts and clouds -------------' +bin/quads-cli --rm-host host01.example.com +bin/quads-cli --rm-host host02.example.com +bin/quads-cli --rm-cloud cloud01 +bin/quads-cli --rm-cloud cloud02 +bin/quads-cli --rm-cloud cloud03 + +echo '------- remove bogus hosts and clouds -------' +bin/quads-cli --rm-host bogus_host +bin/quads-cli --rm-cloud bogus_cloud diff --git a/quads/api_v2.py b/quads/api_v2.py index 2c338dbf7..fb30ddfda 100644 --- a/quads/api_v2.py +++ b/quads/api_v2.py @@ -3,27 +3,32 @@ import urllib from quads import model as m +from quads.model import Cloud -@cherrypy.expose -class MethodHandler(object): - def __init__(self, model, name): +class MethodHandlerBase(object): + def __init__(self, model, name, proprty=None): self.m = model self.name = name + self.proprty = proprty def _get_obj(self, obj): q = {self.name: obj} obj = self.m.objects(**q).first() return obj + +@cherrypy.expose +class DocumentMethodHandler(MethodHandlerBase): def GET(self, **data): args = {} if 'cloudonly' in data: - c = Cloud.objects(cloud=data['cloudonly']).first() + c = Cloud.objects(cloud=data['cloudonly']) if not c: - raise cherrypy.HTTPError(404, - 'Cloud %s Not Found' % data['cloudonly']) + cherrypy.response.status = "404 Not Found" + return json.dumps({'result': + 'Cloud %s Not Found' % data['cloudonly']}) else: - args.update({'cloud': c}) + return c.to_json() return self.m.objects(**args).to_json() # post data comes in **data @@ -37,29 +42,39 @@ def POST(self, **data): result, data = self.m.prep_data(data) # Check if there were data validation errors - if not result: + if result: + result = ['Data validation failed: %s' % ', '.join(result)] + cherrypy.response.status = "400 Bad Request" + else: # check if object already exists obj = self._get_obj(data[self.name]) if obj and not force: result.append('%s %s already exists' % ( self.name, data[self.name])) + cherrypy.response.status = "409 Conflict" else: # Create/update Operation try: # if force and found object do an update if force and obj: + # TODO: DEFAULTS OVERWRITE EXISTING VALUES obj.update(**data) result.append('Updated %s %s' % (self.name, data[self.name])) # otherwise create it else: obj = self.m(**data).save() + cherrypy.response.status = "201 Resource Created" result.append('Created %s %s' % (self.name, data[self.name])) except Exception, e: - result.append(e) - + # TODO: make sure when this is thrown the output + # points back to here and gives the end user + # enough information to fix the issue + cherrypy.response.status = "500 Internal Server Error" + result.append('Error: %s' % e) + print result return json.dumps({'result': result}) def PUT(self, **data): @@ -71,20 +86,67 @@ def DELETE(self, obj_name): obj = self._get_obj(obj_name) if obj: obj.delete() - # TODO: better msg please! result = ['deleted %s %s' % (self.name, obj_name)] else: - raise cherrypy.HTTPError(404, '%s %s Not Found' % ( - self.name, obj)) + cherrypy.response.status = "404 Not Found" + result = ['%s %s Not Found' % (self.name, obj_name)] + return json.dumps({'result': result}) + +@cherrypy.expose +class PropertyMethodHandler(MethodHandlerBase): + def GET(self, **data): + args = {} + return self.m.objects(**args).to_json() + + # post data comes in **data + def POST(self, **data): + # make sure post data passed in is ready to pass to mongo engine + prep_data = getattr(self.m, 'prep_%s_data' % self.proprty) + result, obj, data = prep_data(data) + + # Check if there were data validation errors + if result: + result = ['Data validation failed: %s' % ', '.join(result)] + cherrypy.response.status = "400 Bad Request" + else: + try: + obj.update(**data) + cherrypy.response.status = "201 Resource Created" + result.append('Added %s %s' % (self.proprty, data)) + except Exception, e: + # TODO: make sure when this is thrown the output + # points back to here and gives the end user + # enough information to fix the issue + cherrypy.response.status = "500 Internal Server Error" + result.append('Error: %s' % e) + print result + return json.dumps({'result': result}) + + def PUT(self, **data): + # update operations are done through POST + # using PUT would duplicate most of POST + return self.POST(**data) + + def DELETE(self, item, obj_name): + obj = self._get_obj(obj_name) + if obj: + data = {'unset__%s__%s' % (self.proprty, item): True} + obj.update(**data) + result = ['deleted %s from %s' % (self.proprty, obj_name)] + else: + cherrypy.response.status = "404 Not Found" + result = ['%s Not Found for %s %s' % (self.proprty, self.name, obj_name)] return json.dumps({'result': result}) @cherrypy.expose class QuadsServerApiV2(object): def __init__(self): - self.host = MethodHandler(m.Host, 'host') - self.cloud = MethodHandler(m.Cloud, 'cloud') - self.owner = MethodHandler(m.Cloud, 'owner') - self.ccuser = MethodHandler(m.Cloud, 'ccuser') - self.ticket = MethodHandler(m.Cloud, 'ticket') - self.qinq = MethodHandler(m.Cloud, 'qinq') - self.wipe = MethodHandler(m.Cloud, 'wipe') + self.cloud = DocumentMethodHandler(m.Cloud, 'cloud') + self.owner = DocumentMethodHandler(m.Cloud, 'owner') + self.ccuser = DocumentMethodHandler(m.Cloud, 'ccuser') + self.ticket = DocumentMethodHandler(m.Cloud, 'ticket') + self.qinq = DocumentMethodHandler(m.Cloud, 'qinq') + self.wipe = DocumentMethodHandler(m.Cloud, 'wipe') + self.host = DocumentMethodHandler(m.Host, 'host') + self.schedule = PropertyMethodHandler(m.Host, 'host', 'schedule') + self.interfaces = PropertyMethodHandler(m.Host, 'host', 'interfaces') diff --git a/quads/model.py b/quads/model.py index e6be2a9a6..2a579f0a4 100644 --- a/quads/model.py +++ b/quads/model.py @@ -21,7 +21,6 @@ def param_check(data, params, defaults={}): return result, data class Cloud(Document): - #name = 'cloud' cloud = StringField() description = StringField() owner = StringField() @@ -33,7 +32,6 @@ class Cloud(Document): @staticmethod def prep_data(data): - # hard coded for cloud defaults = {'owner': 'nobody', 'ccuser': [], 'ticket': '000000', @@ -49,11 +47,10 @@ def prep_data(data): class Host(Document): - #name = 'host' host = StringField() cloud = ReferenceField(Cloud, required=True) interfaces = DictField() - schedule = DictField() + schedule = ListField(DictField()) type = StringField() @staticmethod @@ -62,12 +59,51 @@ def prep_data(data): if not result: cloud = Cloud.objects(cloud=data['cloud']).first() if not cloud: - result.append('Cloud %s not found') + result.append('Cloud %s not found' % data['cloud']) else: - data['cloud'] = cloud - + data['cloud'] = cloud + return result, data + @staticmethod + def prep_schedule_data(data): + result, data = param_check(data, ['cloud', 'host', + 'start', 'end']) + host = None + if not result: + cloud = Cloud.objects(cloud=data['cloud']).first() + host = Host.objects(host=data['host']).first() + if not cloud: + result.append('Cloud %s not found' % data['cloud']) + elif not host: + result.append('Host %s not found' % data['host']) + else: + data['cloud'] = cloud + del data['host'] + + data = {'add_to_set__schedule': [data]} + + return result, host, data + + @staticmethod + def prep_interfaces_data(data): + result, data = param_check(data, ['host', 'interface', 'mac', + 'vendor_type', 'port']) + host = None + if not result: + host = Host.objects(host=data['host']).first() + if not host: + result.append('Host %s not found' % data['host']) + else: + del data['host'] + interface = data['interface'] + del data['interface'] + data = {'set__interfaces__%s' % interface: data} + + print result + + return result, host, data + class History(Document): pass