def delete(self, project_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # pull the entry from the db project = Project.get_by_id(long(project_id)) # if we found it and own it if project and project.owner == user_info.key: # patch all wisps using project to stock Wisp.patch_to_stock(project.key) # delete the project project.key.delete() self.add_message('Project successfully deleted!', 'success') else: self.add_message('Project 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, wisp_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # pull the entry from the db wisp = Wisp.get_by_id(long(wisp_id)) # if we found it and own it, delete if wisp and wisp.owner == user_info.key: # delete any associated bids, if they exist InstanceBid.delete_by_wisp(wisp.key) # delete the wisp wisp.key.delete() self.add_message('Wisp successfully deleted!', 'success') else: self.add_message('Wisp was not deleted. Something went horribly wrong somewhere!', 'warning') # hangout for a second time.sleep(1) # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) channel.send_message(channel_token, 'reload') return
def get(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up wisps wisps = Wisp.get_by_user(user_info.key) # redirect if we don't have any wisps if not wisps: return self.redirect_to('account-wisps-new') # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'wisps': wisps, 'refresh_channel': refresh_channel, 'channel_token': channel_token } # check for default default = False for wisp in wisps: if wisp.default: default = True if not default: print "wtf" self.add_message("Please set a wisp to be default!", "error") return self.render_template('wisp/wisps.html', **params)
def get(self): # get older anonymous wisps to delete wisps = Wisp.get_expired_anonymous() if wisps: # remove wisps for wisp in wisps: # delete wisp wisp.key.delete() return
def get(self): # basics ip = self.request.remote_addr # setup channel to do page refresh channel_token = generate_token() refresh_channel = channel.create_channel(channel_token) # various pulldown initialization self.form.flavor.choices=[] self.form.wisp.choices=[] self.form.cloud.choices=[] # add list of user's flavors, if any flavors = Flavor.flavors_with_instances_on_sale() for flavor in flavors: self.form.flavor.choices.insert(0, (flavor.name, flavor.description)) # used for determining element layout wisp = None cloud = None # if the user is logged in, we build out the list of their wisps if self.user_id: # lookup user's auth info and wisps user_info = User.get_by_id(long(self.user_id)) wisps = Wisp.get_by_user(user_info.key) for wisp in wisps: self.form.wisp.choices.insert(0, (wisp.key.id(), wisp.name)) # load clouds clouds = Cloud.get_by_user(user_info.key) # create default cloud if none if not clouds: cloud = Cloud.create_default(user_info.key) clouds.append(cloud) self.add_message("Default cloud created.", 'success') for cloud in clouds: self.form.cloud.choices.insert(0, (cloud.key.id(), cloud.name)) # params build out (injects the last wisp, if there was one) params = { 'remote_ip': ip, 'wisp': wisp, 'cloud': cloud, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('lab/launcher.html', **params)
def get(self, wisp_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the wisp in question wisp = Wisp.get_by_id(long(wisp_id)) # if doesn't exist, redirect if not wisp: return self.redirect_to('account-wisps') # insert images into list for wisp self.form.image.choices=[('custom', "Dynamic Image URL")] images = Image.get_all() for image in images: self.form.image.choices.insert(0, (str(image.key.id()), image.description)) # load form values self.form.name.data = wisp.name self.form.ssh_key.data = wisp.ssh_key self.form.dynamic_image_url.data = wisp.dynamic_image_url self.form.post_creation.data = wisp.post_creation self.form.callback_url.data = wisp.callback_url self.form.default.data = wisp.default # hack up the form a bit if wisp.callback_url: self.form.callback.data = "custom" if wisp.dynamic_image_url: self.form.image.data = "custom" else: self.form.image.data = str(wisp.image.id()) # check if the owner is this user if wisp and wisp.owner == user_info.key: # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'wisp': wisp, 'image': image, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('wisp/wisp_detail.html', **params) else: return self.redirect_to('account-wisps')
def get(self, token=None): # paramters, assume failure, response type # response, type, cross posting params = {} params['response'] = "error" self.response.headers['Content-Type'] = "application/json" self.response.headers['Access-Control-Allow-Origin'] = '*' # look for instance bid first wisp = Wisp.get_by_token(token) # if no instance, then show error if not wisp: params['message'] = "Wisp not found." self.response.set_status(404) return self.render_template('api/response.json', **params) else: params['respose'] = "success" params['wisp'] = wisp return self.render_template('api/wisp.json', **params)
def get(self, wisp_id = None, file = None): # get wisp wisp = Wisp.get_by_id(long(wisp_id)) try: # get the project project = wisp.project.get() # params build out params = { 'project_name': project.name, 'project_url': project.url, 'donation_address': project.address, 'port': project.port, 'state': 1, 'ipv4_address': '127.0.0.1', 'ipv6_address': '::1' } # pull out the meta data from the instance and build into key/values # for key,value in instance.meta_data # params[key] = value # also add api_url #'api_url': '%s/api/v1/instance/' if project: # return proxied github content if file == 'README.md': return self.render_url(project.readme_url, **params) elif file == 'install.sh': return self.render_url(project.install_url, **params) except: pass # default response is we don't have it self.response.set_status(404) return
def get(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up wisps wisps = Wisp.get_by_user(user_info.key) if len(wisps) > 0: wisps_exist = True else: wisps_exist = False # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'wisps_exist': wisps_exist, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('user/status.html', **params)
def delete(self, wisp_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # pull the entry from the db wisp = Wisp.get_by_id(long(wisp_id)) # check if wisp is in use by a cloud # if we found it and own it, delete if wisp and wisp.owner == user_info.key: wisp.key.delete() self.add_message('Wisp successfully deleted!', 'success') else: self.add_message('Wisp 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 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 post(self): # request basics ip = self.request.remote_addr # response, type, cross posting params = {} self.response.headers['Content-Type'] = "application/json" self.response.headers['Access-Control-Allow-Origin'] = '*' # check if this IP has any other bids open instancebid = InstanceBid.get_incomplete_by_ip(ip) # check we have an instancebid already if instancebid: # validate wisp if instancebid.wisp == None: instancebid.key.delete() return error_response(self, "Deleting bid because no wisp was associated.", 403, params) # load the payment address if instancebid.instance: instancebid.address = instancebid.instance.get().address instancebid.ask = instancebid.instance.get().ask else: # we should have an instance assosciated, so bail on this one instancebid.key.delete() return error_response(self, "Deleting bid because no instance was associated.", 403, params) params['response'] = "error" params['message'] = "The calling IP address already has an instance reservation in progress." params['instancebid'] = instancebid self.response.set_status(403) return self.render_template('api/bid.json', **params) # load POSTed JSON try: request = json.loads(self.request.body) except Exception as ex: return error_response(self, "Failure in parsing request JSON.", 403, params) # load optional values or defaults # ipv4 (allow default) if 'requires_ipv4' in request: requires_ipv4 = request['requires_ipv4'] else: requires_ipv4 = 0 # ipv6 (allow default) if 'requires_ipv6' in request: requires_ipv6 = request['requires_ipv6'] else: requires_ipv6 = 0 # providers (allow default) if 'providers' in request: providers = request['providers'] else: providers = [{u'id': 1, u'name': u'All Providers'}] # flavors (required) if 'flavor' in request: flavor_name = request['flavor'] flavor = Flavor.get_by_name(flavor_name) # check if flavor was found if not flavor: return error_response(self, "Flavor not found.", 403, params) else: return error_response(self, "Flavor name is required.", 403, params) # cloud (optional) if 'cloud_id' in request: cloud_id = request['cloud_id'] cloud = Cloud.get_by_id(long(cloud_id)) # check if cloud was found if not cloud: return error_response(self, "Cloud ID not found.", 403, params) else: cloud = None # disallow both a wisp and a callback_url if 'wisp_id' in request and 'callback_url' in request: return error_response(self, "A wisp and a callback URL may not be used together.", 403, params) # require either a wisp or a callback_url if 'wisp_id' not in request and 'callback_url' not in request: return error_response(self, "A valid wisp or a callback URL is required.", 403, params) # load the wisp, if there is one if 'wisp_id' in request: wisp_id = request['wisp_id'] wisp = Wisp.get_by_id(long(wisp_id)) else: wisp = None # load the callback URL, if there is one if 'callback_url' in request: callback_url = request['callback_url'] elif wisp: callback_url = wisp.callback_url else: callback_url = "" # test we have a callback_url or a valid image in the wisp if callback_url > "": try: result = urlfetch.fetch(callback_url, deadline=5) if result.status_code > 399: return error_response(self, "The callback URL is unreachable.", 403, params) # test result's image URL except Exception as ex: return error_response(self, "The callback URL is unreachable.", 403, params) elif wisp: if wisp.image == None and wisp.dynamic_image_url == None and wisp.project == None: return error_response(self, "A valid wisp or a callback URL is required.", 403, params) # grab a new bid hash to use for the new bid token = generate_token(size=16) name = "smr-%s" % generate_token(size=8) # create a new bid instancebid = InstanceBid() instancebid.token = token instancebid.name = name instancebid.need_ipv4_address = bool(requires_ipv4) instancebid.need_ipv6_address = bool(requires_ipv6) instancebid.flavor = flavor.key instancebid.remote_ip = ip instancebid.appliances = providers # providers is already JSON instancebid.status = 0 instancebid.callback_url = callback_url # expires in 5 minutes epoch_time = int(time.time()) instancebid.expires = datetime.fromtimestamp(epoch_time+300) # add wisp, if present if wisp: instancebid.wisp = wisp.key # add cloud, if present if cloud: instancebid.cloud = cloud.key # update instancebid.put() # sleep for dev if config.debug: time.sleep(2) # reserve the instance InstanceBid.reserve_instance_by_token(instancebid.token) # get the address, if you got an instance if instancebid.instance: address = instancebid.instance.get().address ask = instancebid.instance.get().ask else: # no instance was reserved instancebid.key.delete() return error_response(self, "No valid instances were returned.", 403, params) # hack address and ask into instancebid object for template (not stored) instancebid.address = address instancebid.ask = ask # build out the response params['response'] = "success" params['message'] = "A new instance bid has been created." params['instancebid'] = instancebid # return response and include cross site POST headers self.response.set_status(201) return self.render_template('api/bid.json', **params)
def post(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # insert images into list for wisp self.form.image.choices=[('custom', "Dynamic Image URL")] images = Image.get_all() for image in images: self.form.image.choices.insert(0, (str(image.key.id()), image.description)) # check what was returned from form validates if not self.form.validate(): self.add_message("The new wisp form did not validate.", "error") return self.get() # load form values name = self.form.name.data.strip() ssh_key = self.form.ssh_key.data.strip() dynamic_image_url = self.form.dynamic_image_url.data.strip() post_creation = self.form.post_creation.data.strip() callback_url = self.form.callback_url.data.strip() default = self.form.default.data # no strip cause bool # hack up form to deal with custom image if self.form.image.data.strip() == "custom": image = None else: image = Image.get_by_id(int(self.form.image.data.strip())).key # hack up form to deal with custom callback if self.form.callback.data.strip() == "custom": image = None ssh_key = None dynamic_image_url = None post_creation = None else: callback_url = None # check if we have it already if Wisp.get_by_user_name(user_info.key, name): self.add_message("A wisp with that name already exists in this account!", "error") return self.redirect_to('account-wisps') # save the new wisp in our database wisp = Wisp( name = name, owner = user_info.key, image = image, ssh_key = ssh_key, dynamic_image_url = dynamic_image_url, post_creation = post_creation, callback_url = callback_url, ) wisp.put() # set default if true if default: Wisp.set_default(wisp) # log to alert self.add_message("Wisp %s successfully created!" % name, "success") # give it a few seconds to update db, then redirect time.sleep(1) return self.redirect_to('account-wisps')
def post(self, wisp_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # load the wisp in question wisp = Wisp.get_by_id(long(wisp_id)) # if doesn't exist, redirect if not wisp: return self.redirect_to('account-wisps') # insert images into list for wisp self.form.image.choices=[('custom', "Dynamic Image URL")] images = Image.get_all() for image in images: self.form.image.choices.insert(0, (str(image.key.id()), image.description)) # check what was returned from form validates if not self.form.validate(): self.add_message("The new wisp form did not validate.", "error") return self.get(wisp_id = wisp_id) # load form values name = self.form.name.data.strip() ssh_key = self.form.ssh_key.data.strip() dynamic_image_url = self.form.dynamic_image_url.data.strip() post_creation = self.form.post_creation.data.strip() callback_url = self.form.callback_url.data.strip() default = self.form.default.data # hack up form to deal with custom image if self.form.image.data.strip() == "custom": image = None else: image = Image.get_by_id(int(self.form.image.data.strip())).key # hack up form to deal with custom callback if self.form.callback.data.strip() == "custom": image = None ssh_key = None dynamic_image_url = None post_creation = None else: callback_url = None # check if the wisp owner is this user if wisp and wisp.owner == user_info.key: # save the new wisp in our database wisp.name = name wisp.image = image wisp.ssh_key = ssh_key wisp.dynamic_image_url = dynamic_image_url wisp.post_creation = post_creation wisp.callback_url = callback_url wisp.put() # set default if true, or turn it off if false if default: Wisp.set_default(wisp) else: wisp.default = False wisp.put() # log to alert self.add_message("Wisp %s updated!" % name, "success") # give it a few seconds to update db, then redirect time.sleep(1) else: # log to alert self.add_message("Wisp was not updated!", "error") return self.redirect_to('account-wisps')
def get(self, wisp_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the wisp in question wisp = Wisp.get_by_id(long(wisp_id)) # if doesn't exist, redirect if not wisp: return self.redirect_to('account-wisps') # load projects pulldown self.form.project.choices = [] projects = Project.get_available(user_info.key) for project in projects: self.form.project.choices.insert(0, (str(project.key.id()), project.name)) # insert images into list for wisp self.form.image.choices=[('custom', "Dynamic Image URL")] images = Image.get_all() for image in images: self.form.image.choices.insert(0, (str(image.key.id()), image.description)) # load values out of db to show in form self.form.name.data = wisp.name self.form.ssh_key.data = wisp.ssh_key self.form.dynamic_image_url.data = wisp.dynamic_image_url self.form.image_disk_format.data = wisp.image_disk_format self.form.image_container_format.data = wisp.image_container_format self.form.post_creation.data = wisp.post_creation self.form.callback_url.data = wisp.callback_url self.form.default.data = wisp.default # adjust the form's pulldown settings self.form.wisp_type.data = "stock" if wisp.image: self.form.image.data = str(wisp.image.id()) if wisp.callback_url: self.form.wisp_type.data = "custom" if wisp.project: self.form.wisp_type.data = "project" self.form.project.data = str(wisp.project.id()) if wisp.dynamic_image_url: self.form.wisp_type.data = "stock" self.form.image.data = "custom" # check if the owner is this user if wisp and wisp.owner == user_info.key: # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out params = { 'wisp': wisp, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('wisp/edit.html', **params) else: return self.redirect_to('account-wisps')
def post(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # load projects pulldown self.form.project.choices = [] # public + private projects = Project.get_available(user_info.key) for project in projects: self.form.project.choices.insert(0, (str(project.key.id()), project.name)) # insert images into list for wisp self.form.image.choices=[('custom', "Dynamic Image URL")] images = Image.get_all() for image in images: self.form.image.choices.insert(0, (str(image.key.id()), image.description)) # check what was returned from form validates if not self.form.validate(): self.add_message("The new wisp form did not validate.", "error") return self.get() # load form values name = self.form.name.data.strip() ssh_key = self.form.ssh_key.data.strip() dynamic_image_url = self.form.dynamic_image_url.data.strip() image_container_format = self.form.image_container_format.data.strip() image_disk_format = self.form.image_disk_format.data.strip() post_creation = self.form.post_creation.data.strip() callback_url = self.form.callback_url.data.strip() default = self.form.default.data # no strip cause bool # check if project is selected if self.form.wisp_type.data.strip() == 'project': project = Project.get_by_id(long(self.form.project.data.strip())).key else: project = None # hack up form to deal with custom image if self.form.image.data.strip() == "custom": image = None else: image = Image.get_by_id(long(self.form.image.data.strip())).key # hack up form to deal with custom callback if self.form.wisp_type.data.strip() == "custom": image = None ssh_key = None dynamic_image_url = None post_creation = None elif self.form.wisp_type.data.strip() == "project": image = None dynamic_image_url = None post_creation = None else: callback_url = None # check if we have it already if Wisp.get_by_user_name(user_info.key, name): self.add_message("A wisp with that name already exists in this account!", "error") return self.redirect_to('account-wisps') # check if we need to force default setting for first new wisp if not Wisp.get_by_user(user_info.key): default = True # save the new wisp in our database wisp = Wisp( name = name, owner = user_info.key, image = image, ssh_key = ssh_key, dynamic_image_url = dynamic_image_url, image_container_format = image_container_format, image_disk_format = image_disk_format, post_creation = post_creation, callback_url = callback_url, project = project ) wisp.put() # set default if true if default: Wisp.set_default(wisp) # log to alert self.add_message("Wisp %s successfully created!" % name, "success") # give it a few seconds to update db, then redirect time.sleep(1) return self.redirect_to('account-wisps-detail', wisp_id=wisp.key.id())
def post(self): # try to pull get user info from a browser based session if self.user_id: user_info = User.get_by_id(long(self.user_id)) else: user_info = None # paramters, assume failure, response type params = {} params['response'] = "error" self.response.headers['Content-Type'] = "application/json" # response headers self.response.headers['Content-Type'] = "application/json" self.response.headers['Access-Control-Allow-Origin'] = '*' # load various variables body = json.loads(self.request.body) # SSH key (not required) try: ssh_key = body['ssh_key'] except: ssh_key = "" # load project if we have it try: project_id = body['project_id'] project = Project.get_by_id(long(project_id)) except: project = None # handle project wisps differently if project: wisp = Wisp().from_project( ssh_key, project, user_info ) else: try: post_creation = body['post_creation'] except: post_creation = "" try: image_id = body['image_id'] image = Image.get_by_id(long(image_id)) except: try: dynamic_image_url = body['dynamic_image_url'] except: # we don't have an image or a URL, so nothing can do params['message'] = "Wisps require an image to boot." self.response.set_status(401) return self.render_template('api/response.json', **params) # disk and container formats if they were sent (usually qcow/bare) try: image_disk_format = body['image_disk_format'] except: image_disk_format = "qcow2" try: image_container_format = body['image_container_format'] except: image_container_format = "bare" # create an anonymous wisp if we don't have it already wisp = Wisp().from_stock( ssh_key, post_creation, dynamic_image_url, image_disk_format, image_container_format, user_info ) if wisp: # return JSON response params['response'] = "success" params['wisp'] = wisp return self.render_template('api/wisp.json', **params) else: params['message'] = "Wisp creation failed." self.response.set_status(401) return self.render_template('api/response.json', **params)