def get(self): # look up instances that were updated more than 15 minutes ago instances = Instance.get_older_than(900) # loop through results and mark returned instances as 'inactive' if instances: for instance in instances: if instance.state == 1 and instance.reserved == False: logging.info("Marking instance=(%s) stale." % instance.name) instance.state = 0 instance.put() # notification channel if instance.token: channel_message(instance.token, "stale") # look up instances that were updated more than 24 hours ago instances = Instance.get_older_than(86400) # loop through results and delete them if instances: for instance in instances: # whack instances that are decomissioned or never been started if instance.state == 7 or instance.state < 3: logging.info("Deleting stale instance=(%s)." % instance.name) instance.key.delete() # notification channel if instance.token: channel_message(instance.token, "delete") return
def get(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up instances, then patch them for hotstart support pinstances = [] instances = Instance.get_all() for instance in instances: try: username_patch = instance.user.get().username except: username_patch = "#HOTSTART#" # no self pinstance = { "name": instance.name, "status": instance.status, "username_patch": username_patch, "created": instance.created, "expires": instance.expires, "key": instance.key } pinstances.append(pinstance) params = {'instances': pinstances} return self.render_template('admin/instances.html', **params)
def get(self, name=None): # check token token = self.request.get('token') if token != "": user_info = User.get_by_token(token) if user_info: db_instances = Instance.get_all() # work around index warning/errors using a .filter() in models.py instances = [] for db_instance in db_instances: # limit to instances the user has started if db_instance.user == user_info.key: instances.append(db_instance) params = { 'user_name': user_info.username, 'instances': instances } self.response.headers['Content-Type'] = "application/json" return self.render_template('api/instances.json', **params) # no token, no user, no data params = { "response": "fail", "message": "must include [token] parameter with a valid token" } self.response.status = '402 Payment Required' self.response.status_int = 402 self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params)
def get(self, token = None): # lookup up bid bid = InstanceBid.get_by_token(token) if not bid: self.add_message("Instance reservation token %s has expired." % token, 'error') return self.redirect_to('lab-launcher') # grab the instance instance = Instance.get_by_token(token) if not instance: self.add_message("Could not find an instance with token %s." % token, 'error') return self.redirect_to('lab-launcher') # setup channel to do page refresh channel_token = token refresh_channel = channel.create_channel(channel_token) # params build out params = { 'bid': bid, 'instance': instance, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('lab/bid.html', **params)
def get(self, instance_name = None): # get the instance, build the response type instance = Instance.get_by_name(instance_name) self.response.headers['Content-Type'] = "application/json" # if no instance, then show error if not instance: params['message'] = "Instance not found." self.response.set_status(404) return self.render_template('api/response.json', **params) # load the instance's meta data, if any if instance.meta: meta = json.loads(instance.meta) else: meta = json.loads('{}') # build response params = { "instance": instance, "meta": meta } params['response'] = "success" self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/instance.json', **params)
def get(self, demo_name = None, token = None): # grab the instance instance = Instance.get_by_token(token) if not instance: self.add_message("That instance cannot be found.", 'error') return self.redirect_to('demos', demo_name=demo_name) # setup channel to do page refresh channel_token = token refresh_channel = channel.create_channel(channel_token) # hack in time max for timer instance.data_max = int(instance.expires - int(instance.started.strftime('%s'))) # dict the meta if instance.meta: instance.meta_dict = json.loads(instance.meta) else: instance.meta_dict = {} params = { 'instance': instance, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('site/demos/%s_instance.html' % demo_name, **params)
def get(self, cloud_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the cloud in question cloud = Cloud.get_by_id(long(cloud_id)) # bail if cloud doesn't exist or not owned by this user if not cloud or cloud.owner != user_info.key: return self.redirect_to('account-clouds') # look up cloud's instances instances = Instance.get_by_cloud(cloud.key) # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'cloud': cloud, 'instances': instances, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('cloud/edit.html', **params)
def get(self, cloud_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the cloud in question cloud = Cloud.get_by_id(long(cloud_id)) # bail if cloud doesn't exist or not owned by this user if not cloud or cloud.owner != user_info.key: return self.redirect_to('account-clouds') # look up cloud's instances instances = Instance.get_by_cloud(cloud.key) # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'cloud': cloud, 'instances': instances, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('cloud/edit.html', **params)
def delete(self, cloud_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # pull the entry from the db cloud = Cloud.get_by_id(long(cloud_id)) # check the number of instances count = Instance.get_count_by_cloud(cloud.key) # deny if it has instances if count > 0: self.add_message('You may not delete a cloud with instances!', 'info') # if we found it and own it, delete elif cloud and cloud.owner == user_info.key: cloud.key.delete() self.add_message('Cloud successfully deleted!', 'success') else: self.add_message('Cloud was not deleted. Something went horribly wrong somewhere!', 'warning') # hangout for a second time.sleep(1) # use the channel to tell the browser we are done and reload channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return
def delete(self, cloud_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # pull the entry from the db cloud = Cloud.get_by_id(long(cloud_id)) # check the number of instances count = Instance.get_count_by_cloud(cloud.key) # deny if it has instances if count > 0: self.add_message('You may not delete a cloud with instances!', 'info') # if we found it and own it, delete elif cloud and cloud.owner == user_info.key: cloud.key.delete() self.add_message('Cloud successfully deleted!', 'success') else: self.add_message( 'Cloud was not deleted. Something went horribly wrong somewhere!', 'warning') # hangout for a second time.sleep(1) # use the channel to tell the browser we are done and reload channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return
def get(self, name): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up user's instances instance = Instance.get_by_name(name) self.response.headers['Content-Type'] = "text/plain" if not instance: params = {"contents": "Waiting on serial console output..."} return self.render_template('instance/console.txt', **params) try: # update list of instances we have http = httplib2.Http() url = '%s/api/instance/%s/console?token=%s' % ( config.fastener_host_url, name, config.fastener_api_token) response, content = http.request(url, 'GET') stuff = json.loads(content) params = {"contents": stuff['contents']} except Exception as ex: params = {"contents": "Waiting on serial console output..."} return self.render_template('instance/console.txt', **params)
def get(self, name=None): # check token token = self.request.get('token') if token != "": user_info = User.get_by_token(token) if user_info: instance = Instance.get_by_name(name) try: if instance.user == user_info.key: # make the instance call to the control box http = httplib2.Http(timeout=10) url = '%s/api/instance/%s/start?token=%s' % ( config.fastener_host_url, name, config.fastener_api_token) # pull the response back TODO add error handling response, content = http.request(url, 'GET', None, headers={}) # update if google returns pending if json.loads(content)['status'] == "PENDING": instance.status = "STAGING" instance.started = datetime.datetime.now() instance.put() params = {'instance': instance} self.response.headers[ 'Content-Type'] = "application/json" return self.render_template('api/instance.json', **params) except Exception as ex: print "error %s" % ex print "instance %s not found or not in TERMINATED state" % name params = { "response": "fail", "message": "[token] read access denied or instance not TERMINATED" } return self.render_template('api/response.json', **params) # no token, no user, no data params = { "response": "fail", "message": "must include [token] parameter with a valid token" } self.response.status = '402 Payment Required' self.response.status_int = 402 self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params)
def get(self, name=None): # define possible status statuses = [ "PROVISIONING", "STAGING", "CONFIGURING", "RUNNING", "STOPPING", "TERMINATED", "BUILDING" # lucidworks status for building out box ] status = self.request.get('status') if status.upper() not in statuses: params = { "response": "fail", "message": "unknown status %s" % status } return self.render_template('api/response.json', **params) password = self.request.get('password') if password != "": instance = Instance.get_by_password(password) try: if instance.name == name: instance.status = status instance.put() self.response.headers['Content-Type'] = "application/json" params = {'instance': instance} return self.render_template('api/instance.json', **params) else: params = { "response": "fail", "message": "unknown instance name %s" % name } return self.render_template('api/response.json', **params) except Exception as ex: params = { "response": "fail", "message": "exception reached %s" % ex } return self.render_template('api/response.json', **params) else: params = { "response": "fail", "message": "[password] write status access denied" } return self.render_template('api/response.json', **params)
def get(self): db_instances = Instance.get_all() # work around index warning/errors using a .filter() in models.py statuses = [] for db_instance in db_instances: # limit to instances the user has started statuses.append(db_instance.status[0]) params = {'statuses': statuses} self.response.headers['Content-Type'] = "application/json" return self.render_template('api/instances.csv', **params)
def post(self): # paramters, assume failure, response type params = {} params['response'] = "error" self.response.headers['Content-Type'] = "application/json" # get appliance variables try: packet = json.loads(self.request.body) apitoken = packet['appliance']['apitoken'] except: params['message'] = "You must submit a valid JSON object with a token." self.response.set_status(401) return self.render_template('api/response.json', **params) # load the appliance appliance = Appliance.get_by_token(apitoken) if not appliance: params['message'] = "Token is not valid." self.response.set_status(401) return self.render_template('api/response.json', **params) if appliance.activated == False: # appliance not activated params['message'] = "Appliance has been disabled by pool controller. Please contact support." self.response.set_status(409) return self.render_template('api/response.json', **params) # update appliance info latitude = float(packet['appliance']['location']['latitude']) longitude = float(packet['appliance']['location']['longitude']) appliance.location = ndb.GeoPt(latitude, longitude) appliance.dynamicimages = bool(packet['appliance']['dynamicimages']) appliance.put() # loop through instances being advertised for sale for appliance_instance in packet['instances']: # pass in appliance_instance and appliance object #logging.info("instance: %s" % appliance_instance['name']) instance = Instance.push(appliance_instance, appliance) # build parameter list params = {} params['response'] = "success" params['message'] = "Instances accepted for sale." self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/response.json', **params)
def get(self, name): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up user's instances instance = Instance.get_by_name(name) if not instance: params = {} return self.redirect_to('instances-list', **params) if instance.renamed == None: instance.renamed = "" # remap so template can address if instance.created < (datetime.datetime.now() - datetime.timedelta(0, 600)): instance.expired = True else: instance.expired = False stream = Stream.get_by_id(instance.stream.id()) if utils.read_cookie(self, "guide") == "closed": guide = False else: guide = True if instance.size == 1: instance_cores = 8 instance_memory = 30 elif instance.size == 2: instance_cores = 16 instance_memory = 60 else: instance_cores = 4 instance_memory = 15 params = { 'guide': guide, 'instance': instance, 'stream': stream, 'user_info': user_info, 'instance_cores': instance_cores, 'instance_memory': instance_memory } return self.render_template('instance/detail.html', **params)
def get(self, appliance_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # seek out the appliance in question appliance = Appliance.get_by_id(long(appliance_id)) # bail if appliance doesn't exist user isn't the owner if not appliance or appliance.owner != user_info.key: return self.redirect_to('account-appliances') # find instances associated with this appliance instances = Instance.get_by_appliance(appliance.key) # render new appliance page parms = {'appliance': appliance, 'instances': instances} return self.render_template('appliance/view.html', **parms)
def get(self, appliance_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # seek out the appliance in question appliance = Appliance.get_by_id(long(appliance_id)) # bail if appliance doesn't exist user isn't the owner if not appliance or appliance.owner != user_info.key: return self.redirect_to("account-appliances") # find instances associated with this appliance instances = Instance.get_by_appliance(appliance.key) # render new appliance page parms = {"appliance": appliance, "instances": instances} return self.render_template("appliance/view.html", **parms)
def get(self, instance_name = None): # get the instance, build the response type instance = Instance.get_by_name(instance_name) self.response.headers['Content-Type'] = "application/json" # if no instance, then show error if not instance: params['message'] = "Instance not found." self.response.set_status(404) return self.render_template('api/response.json', **params) params = {} params['response'] = "success" self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/instance.json', **params)
def delete(self, token=None): bid = InstanceBid.get_by_token(token) # delete the bid if bid: bid.key.delete() # patch up the respective instance if it's not been started instance = Instance.get_by_token(token) if instance: # only patch instance if it's not been started if instance.state <= 1: instance.reserved = False instance.token = None instance.put() return
def delete(self, instance_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # delete instance instance = Instance.get_by_id(long(instance_id)) slack.slack_message("Instance %s deleted for %s!" % (instance.name, user_info.username)) instance.key.delete() # hangout for a second if config.isdev: time.sleep(1) params = { "response": "success", "message": "instance %s deleted" % instance.name } return self.render_template('api/response.json', **params)
def get(self, token = None): # lookup up bid bid = InstanceBid.get_by_token(token) if not bid: self.add_message("Instance reservation token %s has expired." % token, 'error') return self.redirect_to('projects') # grab the project from the bid project = Project.get_by_id(bid.wisp.get().project.id()) # grab the instance instance = Instance.get_by_token(token) if not instance: self.add_message("All available instance reservations are in use. Please try again in a few minutes.", 'error') return self.redirect_to('projects') # grab and render the README.md file content = urlfetch.fetch('http://10.0.1.80:8079/wisps/6048757061779456/README.md').content readme_html = bleach.clean( markdown.markdown( unicode(content, 'utf-8') ), config.bleach_tags, config.bleach_attributes ) # setup channel to do page refresh channel_token = token refresh_channel = channel.create_channel(channel_token) params = { 'instance': instance, 'bid': bid, 'project': project, 'readme_html': readme_html, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('project/bid.html', **params)
def get(self, sid=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # redirect to a POST if we have a sid in the URL if sid and user_info.email: return self.post(sid) try: if not user_info.email or not user_info.name or not user_info.company: need_more_info = True else: need_more_info = False except: need_more_info = True # look up user's instances db_instances = Instance.get_all() # work around index warning/errors using a .filter() in models.py instances = [] for db_instance in db_instances: # limit to instances the user has started if db_instance.user == user_info.key: if db_instance.renamed == None: db_instance.renamed = "" instances.append(db_instance) streams = Stream.get_all() params = { 'instances': instances, 'num_instances': len(instances), 'streams': streams, 'user_id': self.user_id, 'user_info': user_info, 'sid': sid, 'need_more_info': need_more_info } return self.render_template('instance/list.html', **params)
def get(self, token = None): # grab the instance instance = Instance.get_by_token(token) if not instance: self.add_message("Could not find an instance with token %s." % token, 'error') return self.redirect_to('lab-launcher') # hack in time max for timer instance.data_max = int(instance.expires - int(instance.started.strftime('%s'))) # setup channel to do page refresh channel_token = token refresh_channel = channel.create_channel(channel_token) # params build out params = { 'instance': instance, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('lab/instance.html', **params)
def delete(self, cloud_id = None, instance_id = None): # hangout for a second time.sleep(1) # look up the instance instance = Instance.get_by_id(long(instance_id)) if instance: cloud = instance.cloud if long(cloud.id()) == long(cloud_id): instance.cloud = None instance.put() else: self.add_message("Clouds don't match.", "error") else: self.add_message("Instance not found!", "error") # use the channel to tell the browser we are done and reload channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return
def get(self, token=None): # response, type, cross posting params = {} self.response.headers['Content-Type'] = "application/json" self.response.headers['Access-Control-Allow-Origin'] = '*' # look for instance bid first bid = InstanceBid.get_by_token(token) if bid: # build out the response params['response'] = "success" params['message'] = "Reservation found by token." params['instancebid'] = bid # return response self.response.set_status(201) return self.render_template('api/bid.json', **params) else: # look for instance instance = Instance.get_by_token(token) if instance: # build out the response params['response'] = "success" params['message'] = "Instance found by token." params['instance'] = instance # return response self.response.set_status(201) return self.render_template('api/instancedetail.json', **params) else: # build out the error response params['response'] = "error" params['message'] = "No resources found by token." return self.render_template('api/response.json', **params)
def delete(self, cloud_id=None, instance_id=None): # hangout for a second time.sleep(1) # look up the instance instance = Instance.get_by_id(long(instance_id)) if instance: cloud = instance.cloud if long(cloud.id()) == long(cloud_id): instance.cloud = None instance.put() else: self.add_message("Clouds don't match.", "error") else: self.add_message("Instance not found!", "error") # use the channel to tell the browser we are done and reload channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return
def post(self): # request basics ip = self.request.remote_addr offset = self.request.get("offset") limit = self.request.get("limit") instances = Instance().get_all_offered() # add gravatar URLs for instance in instances: email = instance.appliance.get().owner.get().email gravatar_hash = md5.new(email.lower().strip()).hexdigest() instance.gravatar_url = "https://www.gravatar.com/avatar/%s" % gravatar_hash # build parameter list params = { 'remote_ip': ip, 'instances': instances } # return images via template self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/instances.json', **params)
def get(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up appliances clouds = Cloud.get_by_user(user_info.key) # instance counts for cloud in clouds: count = Instance.get_count_by_cloud(cloud.key) cloud.instance_count = count # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'clouds': clouds, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('cloud/list.html', **params)
def get(self, name=None): # check token token = self.request.get('token') if token != "": user_info = User.get_by_token(token) if user_info: instance = Instance.get_by_name(name) try: if instance.user == user_info.key: params = {'instance': instance} self.response.headers[ 'Content-Type'] = "application/json" return self.render_template('api/instance.json', **params) except Exception as ex: print "error %s" % ex print "instance %s not found" % name params = { "response": "fail", "message": "[token] read access denied" } return self.render_template('api/response.json', **params) # no token, no user, no data params = { "response": "fail", "message": "must include [token] parameter with a valid token" } self.response.status = '402 Payment Required' self.response.status_int = 402 self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params)
def post(self, instance_name): # paramters, assume failure, response type params = {} params['response'] = "error" self.response.headers['Content-Type'] = "application/json" # request basics ip = self.request.remote_addr try: body = json.loads(self.request.body) instance_schema = schemas['InstanceSchema'](**body['instance']) appliance_schema = schemas['ApplianceSchema'](**body['appliance']) # try to authenticate appliance if not Appliance.authenticate(appliance_schema.apitoken.as_dict()): logging.error("%s is using an invalid token(%s) or appliance deactivated." % (ip, appliance_schema.apitoken.as_dict())) return error_response(self, "Token is not valid.", 401, params) # fetch appliance and instance appliance = Appliance.get_by_token(appliance_schema.apitoken.as_dict()) instance = Instance.get_by_name_appliance( instance_schema.name.as_dict(), appliance.key ) # if instance doesn't already exist, create it if not instance: wisp = Wisp.get_user_default(appliance.owner) if not wisp: wisp = Wisp.get_system_default() instance = Instance(wisp=wisp.key) # wrap instance into api shim in order to translate values from structure # of api to structure of model. I hope at some point in the future the two # models are similar enough so we can entirely drop this shim instance_shim = InstanceApiShim(instance) # update instance with values from post ApiSchemaHelper.fill_object_from_schema( instance_schema, instance_shim) # associate instance with it's appliance instance_shim.appliance = appliance except Exception as e: return error_response(self, 'Error in creating or updating instance from ' 'post data, with message {0}'.format(str(e)), 500, {}) # update local instance instance.put() # update appliance ip address hint if instance.state > 3 and instance.ipv4_address: appliance.ipv4enabled = True if instance.state > 3 and instance.ipv6_address: appliance.ipv6enabled = True appliance.put() # sleep for dev if config.debug: time.sleep(1) # send update information to channel if instance.token: output = { "name": instance.name, "token": instance.token, "state": instance.state, } channel.send_message(instance.token, json.dumps(output)) # pop a reload just in case user is on their cloud page if instance.owner: user_info = User.get_by_id(long(instance.owner.id())) channel.send_message(user_info.key.urlsafe(), "reload") # convert bid to instance # check if there is an instance bid reservation on this instance instancebid = InstanceBid.get_by_instance(instance.key) if instancebid: # check for a bid callback_url (entered in the callback field on the launcher) if instancebid.callback_url > "": # put the callback into the instance instance.callback_url = instancebid.callback_url elif instancebid.wisp: # otherwise, get the wisp's callback URL callback_url = instancebid.wisp.get().callback_url # if the wisp has an empty callback URL, populate the instance with the wisp's bid details if callback_url == "" or callback_url == None: instance.wisp = instancebid.wisp instance.owner = instancebid.wisp.get().owner instance.cloud = instancebid.cloud else: # we have a custom callback in the wisp itself, so move to instance instance.callback_url = callback_url # update the instance instance.put() # delete the instance reservation instancebid.key.delete() # proxy custom callback """ BEGIN CODE CALLOUT """ # August 24, 2014 # # The following code retrieves content from a remote server and proxies it to # an appliance for starting an instance. It is at this point an anonymous user's # callback pattern is most venerable to being monitored and manipulated. # # The author of this software issues a warrant canary tweet to @stackape which # should appear on the 1st of each month indicating the absence of a gag order # having been issued to StackMonkey, Utter.io, or Kord Campbell. # # Please monitor the code block between BEGIN CODE CALLOUT and END CODE CALLOUT # if the canary tweet does not appear on schedule. Please monitor any callback # file's retrieval patterns if the canary tweet does not appear on schedule. # Please report any findings to the community sections of the project/site. # # Remember, the code is Open Source. Use that fact to the community's advantage. # # The warrant canary will likely be your only indication a change has possibly # taken place to the system. These changes may or may not enable a government # agency to intercept and monitor instances using a custom URL callback. # # Kord Campbell # if instance.callback_url: try: result = urlfetch.fetch(instance.callback_url, deadline=5) except Exception as ex: logging.error("Error fetching callback URL content.") instance.console_output = "Error fetching callback url=(%s)'s' content. %s" % (instance.callback_url, ex) instance.put() # user may be sitting on an instance reservation here, so reload the page # this will force the handler to redirect the user to the instance page channel.send_message(instance.token, "reload") return error_response(self, "Error fetching callback URL content.", 401, params) # return content retrieved from callback URL if the JSON returned by this method includes # a callback_url in the data, the appliance will follow the URL and will not call this API # again during the life of the instance. self.response.headers['Content-Type'] = 'application/json' self.response.write(json.dumps(json.loads(result.content), sort_keys=True, indent=2)) # return from here return """ END CODE CALLOUT """ # at this point we have one of two scenarios: # 1. an external instance start (registered user with appliance, sans instancebid) # 2. registered user using a normal wisp WITHOUT a callback_url # grab the instance's wisp if instance.wisp: # if instance is using a wisp wisp = Wisp.get_by_id(instance.wisp.id()) else: # no wisp on instance wisp = Wisp.get_user_default(instance.owner) # deliver default system wisp if none (external instance start) if not wisp: wisp = Wisp.get_system_default() # load wisp image if not wisp.use_dynamic_image: image = wisp.image.get() else: image = wisp.get_dynamic_image() # pop the ssh_key into an array if wisp.ssh_key: ssh_keys = [] for line in iter(wisp.ssh_key.splitlines()): ssh_keys.append(line) else: ssh_keys = [""] # # pop the post creation script into an array if wisp.post_creation: post_creation = [] for line in iter(wisp.post_creation.splitlines()): post_creation.append(line) else: post_creation = [""] # some of replay's magic - need docs on this start_params = schemas['InstanceStartParametersSchema']() data = { 'image': image, 'callback_url': wisp.callback_url if wisp.callback_url else "", 'ssh_keys': ssh_keys, 'post_create': post_creation} ApiSchemaHelper.fill_schema_from_object(start_params, data) self.response.set_status(200) self.response.headers['Content-Type'] = 'application/json' # write dictionary as json string self.response.out.write(json.dumps( # retrieve dict from schema start_params.as_dict()))
def post(self, instance_name): # paramters, assume failure, response type params = {} params['response'] = "fail" self.response.headers['Content-Type'] = "application/json" # get appliance variables try: packet = json.loads(self.request.body) apitoken = packet['appliance']['apitoken'] except: params['message'] = "You must submit a valid JSON object with a token." self.response.set_status(401) return self.render_template('api/response.json', **params) # load the appliance appliance = Appliance.get_by_token(apitoken) if not appliance: params['message'] = "Token is not valid." self.response.set_status(401) return self.render_template('api/response.json', **params) if appliance.activated == False: # appliance not activated params['message'] = "Appliance has been disabled by pool controller. Please contact support." self.response.set_status(409) return self.render_template('api/response.json', **params) # pull out the appliance's instance try: appliance_instance = packet['instance'] except: params['response'] = "fail" params['result'] = "JSON instance data not found." self.response.set_status(404) return self.render_template('api/response.json', **params) # grab the instance name and check the url try: name = appliance_instance['name'] # same name? if instance_name != name: raise except: params['response'] = "fail" params['result'] = "JSON instance name needs to match resource URI." self.response.set_status(401) self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/response.json', **params) # grab the rest of the instance info try: # grab the rest of appliance POST data flavor_name = appliance_instance['flavor'] ask = appliance_instance['ask'] expires = datetime.fromtimestamp(appliance_instance['expires']) address = appliance_instance['address'] # bitcoin address except: params['response'] = "fail" params['result'] = "JSON instance data not found. Flavor, ask, expires or address missing." self.response.set_status(404) return self.render_template('api/response.json', **params) # look up the pool's version of this instance instance = Instance.get_by_name_appliance(name, appliance.key) # create a new instance for this appliance because we've never seen it if not instance: instance = Instance().push(appliance_instance, appliance) wisp = Wisp.get_user_default(appliance.owner) instance.wisp = wisp.key instance.put() # grab the instance's wisp if instance.wisp: wisp = Wisp.get_by_id(instance.wisp.id()) else: # we need a decent fallback for how to boot an image without a wisp wisp = Wisp.get_user_default(instance.owner) # get the value or return None if not present dynamic_image_url = wisp.dynamic_image_url if wisp.dynamic_image_url > "" else None callback_url = wisp.callback_url if wisp.callback_url > "" else None image = wisp.image.get().name if wisp.image else None # pop the ssh_key script into an array if wisp.ssh_key: ssh_key = [] for line in iter(wisp.ssh_key.splitlines()): ssh_key.append(line) else: ssh_key = [""] # pop the post creation script into an array if wisp.post_creation: post_creation = [] for line in iter(wisp.post_creation.splitlines()): post_creation.append(line) else: post_creation = [""] # load the instance info back into the response params = { 'response': "success", 'instance_name': name, 'image': image, 'dynamic_image_url': dynamic_image_url, 'callback_url': callback_url, 'ssh_key': ssh_key, 'post_creation': post_creation } self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/instances.json', **params)
def put(self, instance_name = None): # disable csrf check in basehandler csrf_exempt = True # paramters, assume failure, response type params = {} params['response'] = "error" self.response.headers['Content-Type'] = "application/json" # get the instance, build the response type instance = Instance.get_by_name(instance_name) self.response.headers['Content-Type'] = "application/json" # if no instance, then show error if not instance: params['message'] = "Instance not found." self.response.set_status(404) return self.render_template('api/response.json', **params) else: # load the instance's meta data, if any if instance.meta: meta = json.loads(instance.meta) else: meta = json.loads('{}') # load the json from the call try: body = json.loads(self.request.body) # loop through key space and set meta data for key in body: meta[key] = body[key] # dump back into the db instance.meta = json.dumps(meta) instance.put() except Exception as e: params['message'] = "An error was encountered with parsing meta key values: %s." % str(e) self.response.set_status(500) return self.render_template('api/response.json', **params) # send update information to channel if instance.token: output = { "name": instance.name, "token": instance.token, "state": instance.state, "meta": meta } channel.send_message(instance.token, json.dumps(output)) # build response params = { "instance": instance, "meta": json.loads(instance.meta) } params['response'] = "success" self.response.headers['Content-Type'] = 'application/json' return self.render_template('api/instance.json', **params)
def get(self, sid): # know the user user_info = User.get_by_id(long(self.user_id)) # check if we have their email if not user_info.email: self.add_message( 'Please update your email address before starting an instance!', 'warning') return self.redirect_to('account-settings') # look up user's instances db_instances = Instance.get_all() # check the user's limits instance_count = 0 for db_instance in db_instances: # limit to instances the user has started if db_instance.user == user_info.key: instance_count = instance_count + 1 # warn and redirect if limit is reached if (instance_count + 1) > user_info.max_instances: self.add_message( 'Instance limit reached. This account may only start %s instances. Please delete an existing instance to start a new one!' % user_info.max_instances, 'warning') return self.redirect_to('instances-list') # get stream stream = Stream.get_by_sid(sid) try: size = stream.instance_size except: size = 0 ## HOT START # check for a hot start instances = Instance.get_hotstarts() for instance in instances: # fiveminutesago depends on number of seconds at end of this *** fiveminutesago = datetime.datetime.now() - datetime.timedelta( 0, 900) # if this hotstart instance has a matching sid, assign and redirect to it if instance.created < fiveminutesago and instance.stream.get( ).sid == stream.sid and instance.status == "RUNNING": # map to user instance.user = user_info.key instance.hotstart = False instance.put() self.add_message( 'Instance assigned! Use login buttons to access %s.' % stream.name, 'success') slack.slack_message("Instance type %s assigned for %s!" % (stream.name, user_info.username)) return self.redirect_to('instance-detail', name=instance.name) # ## TOH TRATS # make the instance call to the control box http = httplib2.Http(timeout=10) # where and who created it if config.isdev: iuser = "******" % ("dev", user_info.username) else: iuser = "******" % ("prod", user_info.username) url = '%s/api/stream/%s?token=%s&user=%s&size=%s' % ( config.fastener_host_url, sid, config.fastener_api_token, iuser, size) try: # pull the response back TODO add error handling response, content = http.request(url, 'POST', None, headers={}) gcinstance = json.loads(content) name = gcinstance['instance'] password = gcinstance['password'] if name == "failed": raise Exception("Instance start failed.") # set up an instance (note there are two ways to create an instance - see below) instance = Instance(name=name, status="PROVISIONING", user=user_info.key, stream=stream.key, size=size, password=password, expires=datetime.datetime.now() + datetime.timedelta(0, 604800), started=datetime.datetime.now()) instance.put() slack.slack_message("Instance type %s created for %s!" % (stream.name, user_info.username)) # give the db a second to update if config.isdev: time.sleep(1) self.add_message( 'Instance created! Give the system a few minutes to start %s.' % stream.name, 'success') params = {'name': name} return self.redirect_to('instance-detail', **params) except: self.add_message( 'The system is currently busy with other instances. Please try again in a few minutes.', 'warning') return self.redirect_to('instances-list')
def post(self, sid=None): # a POST here is a create instance event # know the user user_info = User.get_by_id(long(self.user_id)) if sid and user_info.email: # get form values stream = Stream.get_by_sid(sid) try: size = stream.instance_size except: size = 0 # look up user's instances db_instances = Instance.get_all() # check the user's limits instance_count = 0 for db_instance in db_instances: # limit to instances the user has started if db_instance.user == user_info.key: instance_count = instance_count + 1 # warn and redirect if limit is reached if (instance_count + 1) > user_info.max_instances: self.add_message( 'Instance limit reached. This account may only start %s instances. Please delete an existing instance to start a new one!' % user_info.max_instances, 'warning') return self.redirect_to('instances-list') ## HOT START # check for a hot start instances = Instance.get_hotstarts() for instance in instances: # fiveminutesago depends on number of seconds at end of this *** fiveminutesago = datetime.datetime.now() - datetime.timedelta( 0, 900) # if this hotstart instance has a matching sid, assign and redirect to it if instance.created < fiveminutesago and instance.stream.get( ).sid == stream.sid and instance.status == "RUNNING": # map to user instance.user = user_info.key instance.hotstart = False instance.put() self.add_message( 'Instance assigned! Use login buttons to access %s.' % stream.name, 'success') slack.slack_message("Instance type %s assigned for %s!" % (stream.name, user_info.username)) return self.redirect_to('instance-detail', name=instance.name) # ## TRATS TOH # make the instance call handle http = httplib2.Http(timeout=10) # where and who created it (labels for google cloud console) if config.isdev: iuser = "******" % ("dev", user_info.username.lower()) else: iuser = "******" % ("prod", user_info.username.lower()) # build url to create new instance from stream url = '%s/api/stream/%s?token=%s&user=%s&size=%s' % ( config.fastener_host_url, sid, config.fastener_api_token, iuser, size) try: # pull the response back TODO add error handling response, content = http.request(url, 'POST', None, headers={}) gcinstance = json.loads(content) name = gcinstance['instance'] password = gcinstance['password'] if name == "failed": raise Exception("Instance start failed.") # set up an instance instance = Instance(name=name, status="PROVISIONING", user=user_info.key, stream=stream.key, size=size, password=password, expires=datetime.datetime.now() + datetime.timedelta(0, 604800), started=datetime.datetime.now()) instance.put() slack.slack_message("Instance type %s created for %s!" % (stream.name, user_info.username)) # give the db a second to update if config.isdev: time.sleep(1) self.add_message( 'Instance created! Grab some coffee and wait for %s to start.' % stream.name, 'success') params = {'name': name} return self.redirect_to('instance-detail', **params) except: self.add_message( 'The system is currently busy with other instances. Please try again in a few minutes.', 'warning') return self.redirect_to('instances-list') else: # email update sumbission if not self.form.validate(): self.add_message( "There were errors validating your email address.", "error") return self.get() email = self.form.email.data.strip() user_info = User.get_by_id(long(self.user_id)) user_info.email = email.strip() user_info.put() if len(email) > 3 and not config.isdev: name = user_info.name try: mc = MarketoClient(config.munchkin_id, config.mclient_id, config.mclient_secret) try: first = name.split()[0] except: first = "" try: last = name.split()[1] except: last = "" try: company = user_info.company except: company = "None" leads = [{ "email": user_info.email, "firstName": first, "lastName": last, "company": company, "leadSource": config.mclient_leadSource }] lead = mc.execute( method='push_lead', leads=leads, lookupField='email', programName=config.mclient_programName, programStatus=config.mclient_programStatus) except Exception as ex: slack.slack_message( "Marketo lead create failed because %s." % ex) slack.slack_message( "We got %s to update their email for instance launch!" % user_info.username) self.add_message("Thank you! Your email has been updated.", 'success') # redirect back to GET on list, but with a sid AND email in place this time to create if sid: return self.redirect_to('streams-start3', sid=sid) else: return self.redirect_to('instances-list')
def get(self, name, command): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get instance instance = Instance.get_by_name(name) if not instance: params = {} return self.redirect_to('instances-list', **params) slack.slack_message( "request for an instance we can't find - SPAMSPAMSPAM") else: # check user owns it or user is admin if user_info.admin != True and long(instance.user.id()) != long( self.user_id): params = { "response": "failure", "message": "instance %s not modifiable by calling user" % name } self.response.set_status(500) slack.slack_message("%s doesn't own %s" % (user_info.username, name)) else: # start the instance if command == "start" and instance.status != "RUNNING": slack.slack_message("firing up %s" % instance.name) try: instance.started = datetime.datetime.now() instance.tender_action = "START" instance.put() params = { "response": "success", "message": "Instance %s marked to be started." % instance.name } slack.slack_message( "updated db for %s with %s" % (instance.name, instance.tender_action)) except Exception as ex: params = {"response": "failure", "message": "%s" % ex} self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params) # add ssh_key to instance elif command == "addkey": # make the instance call to the control box http = httplib2.Http(timeout=10) url = '%s/api/instance/%s/addkey?token=%s&ssh_key=%s&username=%s' % ( config.fastener_host_url, name, config.fastener_api_token, urllib.quote(user_info.ssh_key), user_info.username) try: # pull the response back TODO add error handling response, content = http.request(url, 'GET', None, headers={}) # delete if google returns pending if json.loads(content)['status'] == "SUCCESS": params = { "response": "success", "message": "instance %s updated with key" % name } else: params = { "response": "failure", "message": "instance %s operation failure" % name } response.set_status(500) except: params = { "response": "failure", "message": "instance %s failure" % name } self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params) # just the status elif command == "status": params = {"instance": instance} self.response.headers['Content-Type'] = "application/json" return self.render_template('api/instance.json', **params) # delete the instance - C'est la vie elif command == "delete": instance.key.delete() # let the tender script delete it params = { "response": "success", "message": "Instance marked to be deleted." } self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params) # rename it elif command == "rename": renamed = self.request.get('renamed') instance.renamed = renamed instance.put() params = {"instance": instance} self.response.headers['Content-Type'] = "application/json" return self.render_template('api/instance.json', **params) else: params = { "response": "failure", "message": "bad command, skippy" } self.response.set_status(500) self.response.headers['Content-Type'] = "application/json" return self.render_template('api/response.json', **params)