def delete(self, group_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # find the the entry group = Group.get_by_id(long(group_id)) # this member's membership membership = GroupMembers.get_by_userid_groupid( user_info.key, group.key) # list of users that have the group enabled members = GroupMembers.get_group_users(group.key) # remove this user's membership membership.key.delete() # if this user is not the group owner, we simply notify we are done if not group or group.owner != user_info.key: # use the channel to tell the browser we are done and reload self.add_message('Group was removed from account.', 'success') channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return # was there more than just this member? if len(members) > 1: # find the next user by date and assign them as owner entry = GroupMembers.get_new_owner(user_info.key, group.key) print "new owner is %s" % entry.member new_owner = entry.member group.owner = new_owner group.put() # find member's appliances that match this group and remove appliances = Appliance.get_by_user_group(user_info.key, group.key) for appliance in appliances: appliance.group = None # public group appliance.put() else: # no more members, so delete the group group.key.delete() self.add_message('Group successfully deleted!', 'success') # remove group from any and all appliances (heavy handed) appliances = Appliance.get_by_group(group.key) for appliance in appliances: appliance.group = None # public group appliance.put() # 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, group_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # find the the entry group = Group.get_by_id(long(group_id)) # this member's membership membership = GroupMembers.get_by_userid_groupid(user_info.key, group.key) # list of users that have the group enabled members = GroupMembers.get_group_users(group.key) # remove this user's membership membership.key.delete() # if this user is not the group owner, we simply notify we are done if not group or group.owner != user_info.key: # use the channel to tell the browser we are done and reload self.add_message('Group was removed from account.', 'success') channel_token = self.request.get('channel_token') channel.send_message(channel_token, 'reload') return # was there more than just this member? if len(members) > 1: # find the next user by date and assign them as owner entry = GroupMembers.get_new_owner(user_info.key, group.key) print "new owner is %s" % entry.member new_owner = entry.member group.owner = new_owner group.put() # find member's appliances that match this group and remove appliances = Appliance.get_by_user_group(user_info.key, group.key) for appliance in appliances: appliance.group = None # public group appliance.put() else: # no more members, so delete the group group.key.delete() self.add_message('Group successfully deleted!', 'success') # remove group from any and all appliances (heavy handed) appliances = Appliance.get_by_group(group.key) for appliance in appliances: appliance.group = None # public group appliance.put() # 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, appliance_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # lookup the appliance appliance = Appliance.get_by_id(long(appliance_id)) # group choices pulldown self.form.group.choices = [] # add list of user's groups, if any groups = GroupMembers.get_user_groups(user_info.key) for group in groups: self.form.group.choices.insert(0, (group.key.id(), group.name)) # public group self.form.group.choices.insert(0, ("public", "Public")) self.form.name.data = appliance.name self.form.token.data = appliance.token # hacking the form pulldown with javascript because I'm in a hurry if appliance.group: group_id = appliance.group.get().key.id() else: group_id = "public" # this should work, but doesn't - see javascript in appliance_edit.html self.form.group.data = group_id # render new appliance page parms = {"appliance": appliance, "group_id": group_id, "gform": self.gform} return self.render_template("appliance/edit.html", **parms)
def get(self): geopoints = Appliance().get_geopoints() # return JSON response params = { 'geopoints': geopoints } self.response.headers['Content-Type'] = "application/json" return self.render_template('api/geopoints.json', **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): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up appliances appliances = Appliance.get_by_user(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 = {"appliances": appliances, "refresh_channel": refresh_channel, "channel_token": channel_token} return self.render_template("appliance/list.html", **params)
def get(self, group_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the group in question group = Group.get_by_id(long(group_id)) # scan if this user is a member and/or admin if group.owner == user_info.key: is_admin = True is_member = True # obvious else: is_admin = False is_member = GroupMembers.is_member(user_info.key, group.key) # bail if group doesn't exist or user isn't in the membership list if not group or not is_member: return self.redirect_to('account-groups') # get the members members = GroupMembers.get_group_users(group.key) # create an object with appliance counts per user appliance_count = {} for member in members: # get the appliance counts per user for this group count = Appliance.get_appliance_count_by_user_group( member.key, group.key) appliance_count[member.key.id()] = count # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out - ugly cause instructions/admin stuff params = { 'is_admin': is_admin, 'is_member': is_member, 'group': group, 'members': members, 'appliance_count': appliance_count, 'num_members': len(members), 'gmform': self.gmform, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('groups/group_manage.html', **params)
def get(self): appliances = Appliance().appliances_with_instances_on_sale() # add gravatar URLs for appliance in appliances: email = appliance.owner.get().email gravatar_hash = md5.new(email.lower().strip()).hexdigest() appliance.gravatar_url = "https://www.gravatar.com/avatar/%s" % gravatar_hash # return JSON response params = { 'appliances': appliances, 'message': "This is a list of all active appliances with instances for sale." } self.response.headers['Content-Type'] = "application/json" return self.render_template('api/appliances.json', **params)
def get(self, group_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the group in question group = Group.get_by_id(long(group_id)) # scan if this user is a member and/or admin if group.owner == user_info.key: is_admin = True is_member = True # obvious else: is_admin = False is_member = GroupMembers.is_member(user_info.key, group.key) # bail if group doesn't exist or user isn't in the membership list if not group or not is_member: return self.redirect_to('account-groups') # get the members members = GroupMembers.get_group_users(group.key) # create an object with appliance counts per user appliance_count = {} for member in members: # get the appliance counts per user for this group count = Appliance.get_appliance_count_by_user_group(member.key, group.key) appliance_count[member.key.id()] = count # setup channel to do page refresh channel_token = user_info.key.urlsafe() refresh_channel = channel.create_channel(channel_token) # params build out - ugly cause instructions/admin stuff params = { 'is_admin': is_admin, 'is_member': is_member, 'group': group, 'members': members, 'appliance_count': appliance_count, 'num_members': len(members), 'gmform': self.gmform, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('groups/edit.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 delete(self, appliance_id=None): # delete the entry from the db appliance = Appliance.get_by_id(long(appliance_id)) if appliance: appliance.key.delete() self.add_message("Appliance successfully deleted!", "success") else: self.add_message("Appliance 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, appliance_id = None): # delete the entry from the db appliance = Appliance.get_by_id(long(appliance_id)) if appliance: appliance.key.delete() self.add_message('Appliance successfully deleted!', 'success') else: self.add_message('Appliance 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, 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): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up appliances appliances = Appliance.get_by_user(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 = { 'appliances': appliances, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('appliance/list.html', **params)
def get(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # look up appliances appliances = Appliance.get_by_user(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 = { 'appliances': appliances, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('appliance/appliances.html', **params)
def post(self): # 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) # 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() # respond with success params['response'] = "success" params['message'] = "Appliance token authenticated." return self.render_template('api/response.json', **params)
def delete(self, group_id=None, member_id=None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the group in question group = Group.get_by_id(long(group_id)) member = User.get_by_id(long(member_id)) # get this user's admin rights is_admin = False if group.owner == user_info.key: is_admin = True # bail if group doesn't exist or user is not admin if not group or not is_admin: # log to alert self.add_message("You may not remove this user from group.", "error") else: # look up the other user's group membership membership = GroupMembers.get_by_userid_groupid( member.key, group.key) # kill the membership membership.key.delete() # find member's appliances that match that group and remove appliances = Appliance.get_by_user_group(member.key, group.key) for appliance in appliances: appliance.group = None # public group appliance.put() # log to alert self.add_message("User removed from group!", "success") # 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, appliance_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # lookup the appliance appliance = Appliance.get_by_id(long(appliance_id)) # group choices pulldown self.form.group.choices=[] # add list of user's groups, if any groups = GroupMembers.get_user_groups(user_info.key) for group in groups: print group.key.id() self.form.group.choices.insert(0, (group.key.id(), group.name)) # public group self.form.group.choices.insert(0, ('public', "Public")) self.form.name.data = appliance.name self.form.token.data = appliance.token # hacking the form pulldown with javascript because I'm in a hurry if appliance.group: group_id = appliance.group.get().key.id() else: group_id = "public" # this should work, but doesn't - see javascript in appliance_edit.html self.form.group.data = group_id # render new appliance page parms = { 'appliance': appliance, 'group_id': group_id, 'gform': self.gform } return self.render_template('appliance/appliance_edit.html', **parms)
def delete(self, group_id = None, member_id = None): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # get the group in question group = Group.get_by_id(long(group_id)) member = User.get_by_id(long(member_id)) # get this user's admin rights is_admin = False if group.owner == user_info.key: is_admin = True # bail if group doesn't exist or user is not admin if not group or not is_admin: # log to alert self.add_message("You may not remove this user from group.", "error") else: # look up the other user's group membership membership = GroupMembers.get_by_userid_groupid(member.key, group.key) # kill the membership membership.key.delete() # find member's appliances that match that group and remove appliances = Appliance.get_by_user_group(member.key, group.key) for appliance in appliances: appliance.group = None # public group appliance.put() # log to alert self.add_message("User removed from group!", "success") # 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): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # initialize form choices for group self.form.group.choices = [] # add list of user's groups, if any groups = GroupMembers.get_user_groups(user_info.key) for group in groups: self.form.group.choices.insert(0, (str(group.key.id()), group.name)) # public group self.form.group.choices.insert(0, ("public", "Public")) # check if we are getting a custom group entry if self.form.group.data == "custom": # check if the group exists if Group.get_by_name(self.form.custom.data.strip()): self.add_message("A group with that name already exists!", "error") return self.redirect_to("account-appliances") # make the new group group = Group(name=self.form.custom.data.strip(), owner=user_info.key) group.put() group_key = group.key # create the group member entry groupmember = GroupMembers( group=group_key, member=user_info.key, invitor=user_info.key, active=True # same same ) groupmember.put() # hack the form with new group self.form.group.choices.insert(0, ("custom", "Custom")) else: # grab an existing group if self.form.group.data.strip() == "public": # no group for public appliances group_key = None else: # check membership group = Group.get_by_id(int(self.form.group.data.strip())) if GroupMembers.is_member(user_info.key, group.key): group_key = group.key else: group_key = None # check what was returned from the rest of the form validates if not self.form.validate(): self.add_message("The new appliance form did not validate.", "error") return self.get() # load form values name = self.form.name.data.strip() token = self.form.token.data.strip() # check if we have it already - all that work bitches? if Appliance.get_by_token(token): self.add_message("An appliance with that token already exists!", "error") return self.redirect_to("account-appliances") # save the new appliance in our database appliance = Appliance(name=name, token=token, group=group_key, owner=user_info.key) appliance.put() # log to alert self.add_message("Appliance %s successfully created!" % name, "success") # give it a few seconds to update db, then redirect time.sleep(1) return self.redirect_to("account-appliances")
def post(self): # lookup user's auth info user_info = User.get_by_id(long(self.user_id)) # initialize form choices for group self.form.group.choices = [] # add list of user's groups, if any groups = GroupMembers.get_user_groups(user_info.key) for group in groups: self.form.group.choices.insert(0, (str(group.key.id()), group.name)) # public group self.form.group.choices.insert(0, ('public', "Public")) # check if we are getting a custom group entry if self.form.group.data == "custom": # check if the group exists if Group.get_by_name(self.form.custom.data.strip()): self.add_message("A group with that name already exists!", "error") return self.redirect_to('account-appliances') # make the new group group = Group(name=self.form.custom.data.strip(), owner=user_info.key) group.put() group_key = group.key # create the group member entry groupmember = GroupMembers( group=group_key, member=user_info.key, invitor=user_info.key, # same same active=True) groupmember.put() # hack the form with new group self.form.group.choices.insert(0, ('custom', "Custom")) else: # grab an existing group if self.form.group.data.strip() == 'public': # no group for public appliances group_key = None else: # check membership group = Group.get_by_id(int(self.form.group.data.strip())) if GroupMembers.is_member(user_info.key, group.key): group_key = group.key else: group_key = None # check what was returned from the rest of the form validates if not self.form.validate(): self.add_message("The new appliance form did not validate.", "error") return self.get() # load form values name = self.form.name.data.strip() token = self.form.token.data.strip() # check if we have it already - all that work bitches? if Appliance.get_by_token(token): self.add_message("An appliance with that token already exists!", "error") return self.redirect_to('account-appliances') # save the new appliance in our database appliance = Appliance(name=name, token=token, group=group_key, owner=user_info.key) appliance.put() # log to alert self.add_message("Appliance %s successfully created!" % name, "success") # give it a few seconds to update db, then redirect time.sleep(1) return self.redirect_to('account-appliances')
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 get(self, project_id = None): project = Project.get_by_id(long(project_id)) # if no project if not project: return self.redirect_to('projects') # assume this user is not owner owner = False # print self.render_url(project.json_url, {}) # determine if we can show the project if not project.public: # see if we have a user try: user_info = User.get_by_id(long(self.user_id)) if user_info.key != project.owner: raise Exception else: owner = True except: self.add_message("You must be the owner to do this.", "fail") return self.redirect_to('projects') else: try: user_info = User.get_by_id(long(self.user_id)) if user_info.key == project.owner: owner = True except: user_info = None # define minimum specifications for the instance specs = { 'vpus': project.vpus, 'memory': project.memory, 'disk': project.disk } # empty forms self.form.provider.choices = [] self.form.flavor.choices = [] # plenty? plenty = False # dict of providers providers = {} # find the all public provider's appliances and add to providers object appliances = Appliance.get_by_group(None) for appliance in appliances: providers[appliance.key.id()] = { 'name': "%s (Public)" % appliance.name, 'location': [appliance.location.lat, appliance.location.lon], 'flavors': [] } # if there is a user, find his groups and do the same with appliances in those groups if user_info: groups = Group.get_by_owner(user_info.key) for group in groups: appliances = Appliance.get_by_group(group.key) for appliance in appliances: providers[str(appliance.key.id())] = { 'name': "%s of %s (Hybrid Group)" % (appliance.name, group.name), 'location': [appliance.location.lat, appliance.location.lon], 'flavors': [] } # iterate over the dictionary of providers for provider in providers: # find the list of flavors for this provider which support this project flavors = Flavor.flavors_with_min_specs_by_appliance_on_sale( specs, ndb.Key('Appliance', long(provider)) ) # add provider to the form and the flavors to provider object if flavors exist if flavors: plenty = True # insert this provider's appliance into the form self.form.provider.choices.insert(0, (provider, providers[provider]['name'])) # insert flavors into object for flavor in flavors: providers[provider]['flavors'].append( { 'id': str(flavor.key.id()), 'name': flavor.name, 'description': flavor.description } ) # setup channel to do page refresh channel_token = 'changeme' refresh_channel = channel.create_channel(channel_token) # params build out params = { 'project': project, 'providers': json.dumps(providers), 'plenty': plenty, 'owner': owner, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('project/view.html', **params)
def prepare_appliance(self, val): appliance = Appliance.get_by_token(val.token) return [ ('appliance', appliance.key), ('owner', appliance.owner), ('group', appliance.group),]
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()))