def reserve(self, callback_url, flavor_id): # build response response = {"response": "success", "result": {"message": ""}} # find a willing instance instance = db.session.query(Instances).filter_by( state=1, flavor_id=flavor_id).first() if instance: # set that instance to reserved (active == 10) instance.state = 10 instance.callback_url = callback_url instance.update() # tell the pool we're using it (url must be empty string to tell pool) appliance = Appliance().get() pool_response = pool_instances(instance=self, appliance=appliance) # response response['result'][ 'message'] = "Instance %s marked as reserved." % instance.name response['result']['instance'] = row2dict(instance) response['result']['ask'] = instance.flavor.ask response['result']['address'] = instance.address.address else: response['response'] = "error" response['result']['message'] = "No available instances." return response
def reserve(self, callback_url, flavor_id): # build response response = {"response": "success", "result": {"message": ""}} # find a willing instance instance = db.session.query(Instances).filter_by(state=1, flavor_id=flavor_id).first() if instance: # set that instance to reserved (active == 10) instance.state = 10 instance.callback_url = callback_url instance.update() # tell the pool we're using it (url must be empty string to tell pool) appliance = Appliance().get() pool_response = pool_instances( instance=self, appliance=appliance) # response response['result']['message'] = "Instance %s marked as reserved." % instance.name response['result']['instance'] = row2dict(instance) response['result']['ask'] = instance.flavor.ask response['result']['address'] = instance.address.address else: response['response'] = "error" response['result']['message'] = "No available instances." return response
def sync(self, appliance): # grab addresses from coinbase response = coinbase_get_addresses(appliance=appliance) if response['response'] == "success": remoteaddresses = response['result']['addresses'] # update database with remote addresses for remoteaddress_address in remoteaddresses: # work around coinbase's strange address:address thing remoteaddress = remoteaddress_address['address'] # check if address label is the md5 of our coinbase api key if remoteaddress['label'] == md5.new(appliance.cbapikey).hexdigest(): # see if we have a matching address address = db.session.query(Addresses).filter_by(address=remoteaddress['address']).first() # we don't have the address at coinbase in database if address is None: # create a new address address = Addresses() address.address = remoteaddress['address'] address.token = urlparse(remoteaddress['callback_url']).path.split('/')[-1] address.instance_id = 0 # no instances yet address.subdomain = urlparse(remoteaddress['callback_url']).hostname.split('.')[0] # add and commit address.update(address) # we have the address already and need to update it else: # update address from remote addresses address.address = remoteaddress['address'] address.token = urlparse(remoteaddress['callback_url']).path.split('/')[-1] address.subdomain = urlparse(remoteaddress['callback_url']).hostname.split('.')[0] # add and commit address.update(address) else: # must be another appliance's address so skip it pass # overload the results with the list of current addresses response['result']['addresses'] = [] addresses = db.session.query(Addresses).all() for address in addresses: response['result']['addresses'].append(row2dict(address)) return response # failure contacting server else: app.logger.error("Error contacting Coinbase during sync.") # lift respose from server call to view return response
def flavor_error_response(message, flavor=None): # response response['response'] = "error" response['result']['flavor'] = row2dict(flavor) response['result']['message'] = "%s" % message # disable flavor flavor.osid = "" flavor.active = 0 flavor.update() # log it app.logger.error("Failed to install flavor=(%s) into the OpenStack cluster. %s" % (flavor.name, message)) return response
def configure_flavors_detail(flavor_id): # get the matching flavor flavor = db.session.query(Flavors).filter_by(id=flavor_id).first() # handle a GET if request.method == 'GET': # check configuration settings = Status().check_settings() # how much is a micro BTC? try: quote = float( coinbase_get_quote( currency='btc_to_usd')['result']['btc_to_usd']) / 1000000 except: quote = 0 return render_template('/configure/flavor_detail.html', settings=settings, quote=quote, flavor=flavor) # handle a PUT elif request.method == 'PUT': # clear settings cache Status().flush() try: state = int(request.form['enable']) flavor.active = state # set instances with this flavor to the state instances = Instances() instances.toggle(flavor.id, state) except: pass try: ask = request.form['ask'] flavor.ask = ask except: pass # update entry flavor.update() return jsonify({"response": "success", "flavor": row2dict(flavor)})
def flavor_error_response(message, flavor=None): # response response['response'] = "error" response['result']['flavor'] = row2dict(flavor) response['result']['message'] = "%s" % message # disable flavor flavor.osid = "" flavor.active = 0 flavor.update() # log it app.logger.error( "Failed to install flavor=(%s) into the OpenStack cluster. %s" % (flavor.name, message)) return response
def configure_flavors_detail(flavor_id): # get the matching flavor flavor = db.session.query(Flavors).filter_by(id=flavor_id).first() # handle a GET if request.method == "GET": # check configuration settings = Status().check_settings() # how much is a micro BTC? try: quote = float(coinbase_get_quote(currency="btc_to_usd")["result"]["btc_to_usd"]) / 1000000 except: quote = 0 return render_template("/configure/flavor_detail.html", settings=settings, quote=quote, flavor=flavor) # handle a PUT elif request.method == "PUT": # clear settings cache Status().flush() try: state = int(request.form["enable"]) flavor.active = state # set instances with this flavor to the state instances = Instances() instances.toggle(flavor.id, state) except: pass try: ask = request.form["ask"] flavor.ask = ask except: pass # update entry flavor.update() return jsonify({"response": "success", "flavor": row2dict(flavor)})
def sync(self, appliance): # grab image list from pool server response = pool_connect(method="images", appliance=appliance) if response['response'] == "success": remoteimages = response['result'] # update database for images for remoteimage in remoteimages['images']: # see if we have a matching image image = db.session.query(Images).filter_by(name=remoteimage['name']).first() # check if we need to delete image from local db # b'001000' indicates delete image # TODO: need to cleanup OpenStack images if we uninstall if (remoteimage['flags'] & 8) == 8: # only delete if we have it if image is not None: # try to delete the local copy uninstall_image(image) # remove the image from the database image.delete(image) else: # we don't have it, so we do nothing pass # we don't have the image coming in from the server, so install elif image is None: # create a new image image = Images() epoch_time = int(time.time()) image.created = epoch_time image.updated = epoch_time image.name = remoteimage['name'] image.description = remoteimage['description'] image.url = remoteimage['url'] image.size = remoteimage['size'] # used as a suggestion of size only image.cache = 1 # cache locally (unlike dynamic images) image.diskformat = remoteimage['diskformat'] image.containerformat = remoteimage['containerformat'] image.active = 1 # indicates we know about it, but not downloaded image.flags = remoteimage['flags'] # add and commit image.update(image) else: # update image from remote images (local lookup was by name) epoch_time = int(time.time()) image.description = remoteimage['description'] if image.url != remoteimage['url']: image.url = remoteimage['url'] image.updated = epoch_time if image.diskformat != remoteimage['diskformat']: image.diskformat = remoteimage['diskformat'] image.updated = epoch_time if image.containerformat != remoteimage['containerformat']: image.containerformat = remoteimage['containerformat'] image.updated = epoch_time if image.flags != remoteimage['flags']: image.flags = remoteimage['flags'] image.updated = epoch_time # update image.update(image) # grab a new copy of the images in database images = db.session.query(Images).all() # overload the results with the list of current images response['result']['images'] = [] images = db.session.query(Images).all() for image in images: response['result']['images'].append(row2dict(image)) return response # failure contacting server else: # lift respose from server call to view return response
def coinop(self, amount): # build response response = {"response": "success", "result": {"message": "", "instance": {}}} # calculate the purchased seconds based on payment we received ask = float(self.flavor.ask)/1000000 # BTC per hour try: purchased_seconds = (amount/ask)*3600 # amount in BTC/ask in BTC * seconds in hour except: purchased_seconds = 0 # handle local appliance start if amount == 0: purchased_seconds = 15*60 # give 15 minutes to instance for free # current UTC time in seconds since epoch epoch_time = int(time.time()) # if we're not running (state==1 or 10), set the run state to light (to be started) # if we're suspended (state==5), set the run state to relight (to be unsuspended) # cron jobs will take care of the rest of the job of starting/unsuspending # NOTE: We're getting paid pennies for doing nothing until cronjob runs! if self.state == 1 or self.state == 10: self.state = 2 self.expires = epoch_time + purchased_seconds # starting from now self.updated = epoch_time elif self.state == 5: self.state = 6 self.expires = epoch_time + purchased_seconds # starting from now self.updated = epoch_time else: # states 0, 2, 3, 4, 6, 7 self.expires = self.expires + purchased_seconds # starting from expire time self.updated = epoch_time # get instance console output - only run if we've got an osid # basically this only runs when we get a repayment if self.osid: from webapp.libs.openstack import instance_console response = instance_console(self) if 'console' in response['result']: self.console = response['result']['console'] # update the instance self.update() # make a call to the callback url to report instance details callback_url = self.callback_url appliance = Appliance().get() pool_response = pool_instances( url=callback_url, instance=self, appliance=appliance) if pool_response['response'] == "success": # overload response response['result']['message'] = "Added %s seconds to %s's expire time." % (purchased_seconds, self.name) response['result']['instance'] = row2dict(self) else: # note the error in the instance object self.message_count = self.message_count + 1 self.message = pool_response['result']['message'] self.update() # load response and log response = pool_response app.logger.error("Error sending instance=(%s) data to pool." % self.name) return response
def pool_salesman(instances=None, appliance=None): from webapp.libs.openstack import get_stats # form the URL to advertise instance for sale url = "%s/api/v1/broker/" % (app.config['POOL_APPSPOT_WEBSITE']) # grab the cluster's stats try: response = get_stats() stats = response['result']['stats'] except: stats = {} # build the sales packet packet = { "appliance": { "apitoken": appliance.apitoken, "dynamicimages": appliance.dynamicimages, "location": { "latitude": appliance.latitude, "longitude": appliance.longitude }, "stats": stats }, "instances": [] } # response response = {"response": "success", "result": {"message": ""}} # loop through advertised instances for instance in instances: try: # convert to a dict pool_instance = row2dict(instance) # patch in flavor, ask, default image, address pool_instance['flavor'] = { "vpus": instance.flavor.vpus, "memory": instance.flavor.memory, "disk": instance.flavor.disk, "network_up": instance.flavor.network_up, "network_down": instance.flavor.network_down, "ask": instance.flavor.ask } pool_instance['state'] = instance.state pool_instance['address'] = instance.address # add instances to the data packet packet['instances'].append(pool_instance) except: # something didn't go right somewhere, so just nail the instance app.logger.error("Instance=(%s) integrity error." % instance.name) # instance.delete(instance) try: request = Request(url) request.add_header('Content-Type', 'application/json') response = json.loads( urlopen(request, json.dumps(packet), timeout=10).read()) app.logger.info( "Appliance has placed quantity=(%s) instances up for sale." % len(instances)) except HTTPError as ex: response['response'] = "error" response['result'][ 'message'] = "Error code %s returned from server." % ex.code except IOError as ex: response['response'] = "error" response['result'][ 'message'] = "Can't contact callback server. Try again later." except ValueError as ex: response['response'] = "error" response['result'][ 'message'] = "Having issues parsing JSON from the site: %s. Open a ticket." % type( ex).__name__ except Exception as ex: response['response'] = "error" response['result'][ 'message'] = "An error of type %s has occured. Open a ticket." % type( ex).__name__ return response
def configure_flavors_detail(flavor_id): # get the matching flavor flavor = db.session.query(Flavors).filter_by(id=flavor_id).first() # clear settings cache Status().flush() # enable/diskable if 'enable' in request.form.keys(): flavor.update(active=int(request.form['enable'])) # set ask if 'ask' in request.form.keys(): flavor.update(ask=int(request.form['ask'])) # set max-instances if 'max-instances' in request.form.keys(): flavor.update(max_instances=int(request.form['max-instances'])) # install pool flavor if 'install' in request.form.keys(): # let's see what we can break, er install try: if not flavor: response = jsonify({ "response": "error", "result": { "message": "Flavor %s not found." % flavor_id } }) response.status_code = 404 return response # we are told to install if int(request.form['install']): response = flavor_verify_install(flavor) if not response['response'] == 'success': raise Exception(response['result']['message']) if flavor.ask > 0: flavor.update(active=True) else: flavor.update(active=False) else: # we are told to uninstall (install=0) response = flavor_uninstall(flavor) if not response['response'] == 'success': raise Exception(response['result']['message']) flavor.update(installed=False, active=False, osid=None) except Exception as e: response = jsonify({ "response": "error", "result": { "message": str(e) } }) response.status_code = 403 return response # set instance state to match flavor's state instances = Instances() instances.toggle(flavor.id, flavor.active) # update the ask prices on the openstack cluster using metadata try: # get current ask price on openstack and update flavor.save() # warn because we couldn't update the ask price that's set on openstack except nova_exceptions.Forbidden: app.logger.warning( 'No permissions to update price of flavor "{0}".'.format( flavor.name)) return response # handle any other exception while talking to openstack except Exception as e: app.logger.warning('Error updating price of flavor "{0}": {1}.'.format( flavor.name, str(e))) return jsonify({"response": "success", "flavor": row2dict(flavor)})
def pool_salesman(instances=None, appliance=None): from webapp.libs.openstack import get_stats # form the URL to advertise instance for sale url = "%s/api/v1/broker/" % ( app.config['POOL_APPSPOT_WEBSITE'] ) # grab the cluster's stats try: response = get_stats() stats = response['result']['stats'] except: stats = {} # build the sales packet packet = { "appliance": { "apitoken": appliance.apitoken, "dynamicimages": appliance.dynamicimages, "location": { "latitude": appliance.latitude, "longitude": appliance.longitude }, "stats": stats }, "instances": [] } # response response = {"response": "success", "result": {"message": ""}} # loop through advertised instances for instance in instances: try: # convert to a dict pool_instance = row2dict(instance) # patch in flavor, ask, default image, address pool_instance['flavor'] = instance.flavor.name pool_instance['ask'] = instance.flavor.ask pool_instance['state'] = instance.state pool_instance['image'] = instance.image.name pool_instance['address'] = instance.address.address # add instances to the data packet packet['instances'].append(pool_instance) except: # something didn't go right somewhere, so just nail the instance app.logger.error("Instance=(%s) integrity error." % instance.name) instance.delete(instance) try: request = Request(url) request.add_header('Content-Type', 'application/json') response = json.loads(urlopen(request, json.dumps(packet), timeout=10).read()) app.logger.info("Appliance has placed quantity=(%s) instances up for sale." % len(instances)) except HTTPError as ex: response['response'] = "error" response['result']['message'] = "Error code %s returned from server." % ex.code except IOError as ex: response['response'] = "error" response['result']['message'] = "Can't contact callback server. Try again later." except ValueError as ex: response['response'] = "error" response['result']['message'] = "Having issues parsing JSON from the site: %s. Open a ticket." % type(ex).__name__ except Exception as ex: response['response'] = "error" response['result']['message'] = "An error of type %s has occured. Open a ticket." % type(ex).__name__ return response
def sync(self, appliance): # grab image list from pool server response = pool_connect(method="flavors", appliance=appliance) # remote sync if response['response'] == "success": remoteflavors = response['result'] # update the database with the flavors for remoteflavor in remoteflavors['flavors']: flavor = db.session.query(Flavors).filter_by(name=remoteflavor['name']).first() # check if we need to delete flavor from local db # b'001000' indicates delete image # TODO: need to cleanup OpenStack flavor if we uninstall if (remoteflavor['flags'] & 8) == 8: # only delete if we have it if flavor is not None: # remove the flavor from the database flavor.delete(flavor) else: # we don't have it, so we do nothing pass elif flavor is None: # we don't have the flavor coming in from the server flavor = Flavors() # create a new flavor flavor.name = remoteflavor['name'] flavor.description = remoteflavor['description'] flavor.vpus = remoteflavor['vpus'] flavor.memory = remoteflavor['memory'] flavor.disk = remoteflavor['disk'] flavor.network = remoteflavor['network'] flavor.rate = remoteflavor['rate'] flavor.ask = remoteflavor['rate'] # set ask to market rate flavor.hot = remoteflavor['hot'] flavor.launches = remoteflavor['launches'] flavor.flags = remoteflavor['flags'] flavor.active = 1 # add and commit flavor.update(flavor) # we have the flavor and need to update it else: # we have the flavor already, so update flavor.name = remoteflavor['name'] flavor.description = remoteflavor['description'] flavor.vpus = remoteflavor['vpus'] flavor.memory = remoteflavor['memory'] flavor.disk = remoteflavor['disk'] flavor.network = remoteflavor['network'] flavor.rate = remoteflavor['rate'] flavor.hot = remoteflavor['hot'] # we leave flavor.ask alone # we leave flavor.active alone flavor.launches = remoteflavor['launches'] flavor.flags = remoteflavor['flags'] # update flavor.update(flavor) # overload the results with the list of current flavors response['result']['flavors'] = [] flavors = db.session.query(Flavors).all() for flavor in flavors: response['result']['flavors'].append(row2dict(flavor)) return response
def flavor_verify_install(flavor): # build the response response = {"response": "", "result": {"message": "", "flavor": {}}} # get the cluster configuration try: openstack = db.session.query(OpenStack).first() # what happens if they haven't configured it already? if not openstack: raise OpenStackConfiguration( "OpenStack configuration isn't complete.") except Exception as ex: # return error flavor_error_response(ex) # establish connection to openstack try: nova = nova_connection() except Exception as ex: # return error flavor_error_response(ex) # look up flavors try: targetflavor = None # look up the flavor by name and stop on it osflavors = nova.flavors.list() for osflavor in osflavors: if osflavor.name == flavor.name: targetflavor = osflavor break except: # no flavor found targetflavor = None # check for install needed install_flavor = False # check flavor specs match if targetflavor: if targetflavor.vcpus != flavor.vpus: # vpus wrong install_flavor = True if targetflavor.disk != flavor.disk: # disk size wrong install_flavor = True if targetflavor.ram != flavor.memory: # memory wrong install_flavor = True # get the flavor network quota keys (if required) try: if flavor.network > 0: # get the flavor keys from openstack # throws not found if they don't exist osikeys = targetflavor.get_keys() # check quotas match if 'quota:inbound_average' in osikeys and 'quota:outbound_average' in osikeys: if flavor.network != int(osikeys['quota:inbound_average']): install_flavor = True if flavor.network != int( osikeys['quota:outbound_average']): install_flavor = True else: install_flavor = True else: # do nothing pass except: # just force install install_flavor = True else: # no flavor found install_flavor = True app.logger.info("Flavor not found.") # install the flavor if install_flavor: if targetflavor: try: # remove the old flavor nova.flavors.delete(targetflavor.id) except: app.logger.info( "Could not remove the old flavor=(%s) from the OpenStack cluster." % flavor.name) # referenced from ticket #80 # create the new flavor targetflavor = nova.flavors.create(flavor.name, flavor.memory, flavor.vpus, flavor.disk, flavorid='auto', ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True) # set bandwidth targetflavor.set_keys({"quota:inbound_average": flavor.network}) targetflavor.set_keys({"quota:outbound_average": flavor.network}) app.logger.info("Installed flavor=(%s) into the OpenStack cluster." % flavor.name) # update the flavor database with id flavor.osid = targetflavor.id flavor.update(flavor) # response response['response'] = "success" response['result']['message'] = "Flavor added." response['result']['flavor'] = row2dict(flavor) return response
def image_verify_install(image): # build the response response = {"response": "", "result": {"message": "", "image": {}}} # get the cluster configuration openstack = db.session.query(OpenStack).first() # no openstack settings if not openstack: response['response'] = "success" response['result'][ 'message'] = "OpenStack configuration isn't complete." return response # authenticate with keystone to get glance endpoint keystone = ksclient.Client(auth_url=openstack.authurl, username=openstack.osusername, password=openstack.ospassword, tenant_id=openstack.tenantid) # establish connection to glance glance_endpoint = keystone.service_catalog.url_for(service_type='image') glance = glanceclient.Client('1', endpoint=glance_endpoint, token=keystone.auth_token, timeout=10) # flags, bleh. glance, double bleh. install_image = False installed = False # see if we have the image already try: osimage = glance.images.get(image.osid) if osimage: app.logger.info("OpenStack shows an image matching image=(%s)" % image.name) # os image was deleted somehow if osimage.deleted == True: osimage = None app.logger.info("OpenStack shows image=(%s) has been deleted." % image.name) install_image = True except Exception as ex: # thrown if we don't have it installed osimage = None app.logger.info("Image=(%s) needs to be installed." % (image.name)) install_image = True # check if it's old if osimage: # check update times pattern = '%Y-%m-%dT%H:%M:%S' image_epoch = int( time.mktime(time.strptime(osimage.created_at, pattern))) # if we have an old copy in openstack, we delete it and install new if image_epoch < image.updated: app.logger.info( "Deleting image=(%s) from the OpenStack cluster. It was old." % image.name) install_image = True glance.images.delete(image.osid) else: installed = True # check if we have to install if install_image: # test for a local url and set location if image.local_url == "" or image.local_url is None: location = image.url else: location = image.local_url # try to install with either local or remote image try: osimage = glance.images.create( name=image.name, is_public=False, disk_format=image.diskformat, container_format=image.containerformat, location=location) # check if installed if osimage: installed = True except Exception as ex: # glance threw an error, so we assume it was because of the URL app.logger.info( "Trying to use the remote URL for installing the image=(%s)" % image.name) if location == image.local_url: # failure to grab local copy, so try original try: osimage = glance.images.create( name=image.name, is_public=False, disk_format=image.diskformat, container_format=image.containerformat, location=image.url) # check if installed if osimage: app.logger.info("Image=(%s) was installed." % image.name) installed = True except Exception as ex: # nothing can be done installed == False app.logger.info("Glance can't install image=(%s): %s" % (image.name, ex)) else: # we already tried the original URL and it's not working installed == False app.logger.info("Glance can't install image=(%s)." % image.name) # check if we got it installed if installed == False: try: app.logger.info( "The image=(%s) is being deleted from the OpenStack cluster." % image.name) glance.images.delete(image.osid) except Exception as ex: # image.osid didn't exist, or image wasn't installed pass # zero this image on the appliance image.osid = "" image.active = 0 image.update() # response response['response'] = "error" response['result']['image'] = row2dict(image) response['result'][ 'message'] = "Failed to install image into the cluster." app.logger.error( "Failed to install image=(%s) into the OpenStack cluster." % (image.name)) else: # install success! update updated time for image pattern = '%Y-%m-%dT%H:%M:%S' image.updated = int( time.mktime(time.strptime(osimage.created_at, pattern))) image.osid = osimage.id image.update() # response response['response'] = "success" response['result']['image'] = row2dict(image) response['result']['message'] = "Image installed." return response
def flavor_verify_install(flavor): # build the response response = {"response": "", "result": {"message": "", "flavor": {}}} # get the cluster configuration try: openstack = db.session.query(OpenStack).first() # what happens if they haven't configured it already? if not openstack: raise OpenStackConfiguration( "OpenStack configuration isn't complete.") except Exception as ex: # return error flavor_error_response(ex) # establish connection to openstack try: nova = nova_connection() except Exception as ex: # return error flavor_error_response(ex) # look up flavors try: # look up the flavor by name and stop on it targetflavor = nova.flavors.get(flavor.osid) except: # flavor could not be retreived by osid targetflavor = None osflavors = nova.flavors.list() for osflavor in osflavors: if osflavor.name == flavor.name: if flavor.is_same_as_osflavor(osflavor): targetflavor = osflavor break if not targetflavor: try: # referenced from ticket #80 # create the new flavor targetflavor = nova.flavors.create(flavor.name, flavor.memory, flavor.vpus, flavor.disk, flavorid='auto', ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True) except nova_exceptions.Forbidden: response['response'] = "forbidden" response['result'][ 'message'] = "Can't install flavor due to lack of permissions for tenant user." return response except Exception as ex: response['response'] = "error" response['result'][ 'message'] = "Error installing flavor inside OpenStack. %s" % ex return response # set bandwidth targetflavor.set_keys({"quota:inbound_average": flavor.network_down}) targetflavor.set_keys({"quota:outbound_average": flavor.network_up}) app.logger.info("Installed flavor=(%s) into the OpenStack cluster." % flavor.name) # update the flavor database with id flavor.update(osid=targetflavor.id, installed=True) # response response['response'] = "success" response['result']['message'] = "Flavor added." response['result']['flavor'] = row2dict(flavor) return response
def coinop(self, amount): # build response response = { "response": "success", "result": { "message": "", "instance": {} } } # calculate the purchased seconds based on payment we received ask = float(self.flavor.ask) / 1000000 # BTC per hour try: purchased_seconds = ( amount / ask) * 3600 # amount in BTC/ask in BTC * seconds in hour except: purchased_seconds = 0 # handle local appliance start if amount == 0: purchased_seconds = 15 * 60 # give 15 minutes to instance for free # current UTC time in seconds since epoch epoch_time = int(time.time()) # if we're not running (state==1 or 10), set the run state to light (to be started) # if we're suspended (state==5), set the run state to relight (to be unsuspended) # cron jobs will take care of the rest of the job of starting/unsuspending # NOTE: We're getting paid pennies for doing nothing until cronjob runs! if self.state == 1 or self.state == 10: self.state = 2 self.expires = epoch_time + purchased_seconds # starting from now self.updated = epoch_time elif self.state == 5: self.state = 6 self.expires = epoch_time + purchased_seconds # starting from now self.updated = epoch_time else: # states 0, 2, 3, 4, 6, 7 self.expires = self.expires + purchased_seconds # starting from expire time self.updated = epoch_time # get instance console output - only run if we've got an osid # basically this only runs when we get a repayment if self.osid: from webapp.libs.openstack import instance_console response = instance_console(self) if 'console' in response['result']: self.console = response['result']['console'] # update the instance self.update() # make a call to the callback url to report instance details callback_url = self.callback_url appliance = Appliance().get() pool_response = pool_instances(url=callback_url, instance=self, appliance=appliance) if pool_response['response'] == "success": # overload response response['result'][ 'message'] = "Added %s seconds to %s's expire time." % ( purchased_seconds, self.name) response['result']['instance'] = row2dict(self) else: # note the error in the instance object self.message_count = self.message_count + 1 self.message = pool_response['result']['message'] self.update() # load response and log response = pool_response app.logger.error("Error sending instance=(%s) data to pool." % self.name) return response
def image_verify_install(image): # build the response response = {"response": "", "result": {"message": "", "image": {}}} # get the cluster configuration openstack = db.session.query(OpenStack).first() # no openstack settings if not openstack: response['response'] = "success" response['result']['message'] = "OpenStack configuration isn't complete." return response # authenticate with keystone to get glance endpoint keystone = ksclient.Client( auth_url = openstack.authurl, username = openstack.osusername, password = openstack.ospassword, tenant_id = openstack.tenantid ) # establish connection to glance glance_endpoint = keystone.service_catalog.url_for(service_type='image') glance = glanceclient.Client('1', endpoint=glance_endpoint, token=keystone.auth_token, timeout=10) # flags, bleh. glance, double bleh. install_image = False installed = False # see if we have the image already try: osimage = glance.images.get(image.osid) if osimage: app.logger.info("OpenStack shows an image matching image=(%s)" % image.name) # os image was deleted somehow if osimage.deleted == True: osimage = None app.logger.info("OpenStack shows image=(%s) has been deleted." % image.name) install_image = True except Exception as ex: # thrown if we don't have it installed osimage = None app.logger.info("Image=(%s) needs to be installed." % (image.name)) install_image = True # check if it's old if osimage: # check update times pattern = '%Y-%m-%dT%H:%M:%S' image_epoch = int(time.mktime(time.strptime(osimage.created_at, pattern))) # if we have an old copy in openstack, we delete it and install new if image_epoch < image.updated: app.logger.info("Deleting image=(%s) from the OpenStack cluster. It was old." % image.name) install_image = True glance.images.delete(image.osid) else: installed = True # check if we have to install if install_image: # test for a local url and set location if image.local_url == "" or image.local_url is None: location = image.url else: location = image.local_url # try to install with either local or remote image try: osimage = glance.images.create( name = image.name, is_public = False, disk_format = image.diskformat, container_format = image.containerformat, location = location ) # check if installed if osimage: installed = True except Exception as ex: # glance threw an error, so we assume it was because of the URL app.logger.info("Trying to use the remote URL for installing the image=(%s)" % image.name) if location == image.local_url: # failure to grab local copy, so try original try: osimage = glance.images.create( name = image.name, is_public = False, disk_format = image.diskformat, container_format = image.containerformat, location = image.url ) # check if installed if osimage: app.logger.info("Image=(%s) was installed." % image.name) installed = True except Exception as ex: # nothing can be done installed == False app.logger.info("Glance can't install image=(%s): %s" % (image.name, ex)) else: # we already tried the original URL and it's not working installed == False app.logger.info("Glance can't install image=(%s)." % image.name) # check if we got it installed if installed == False: try: app.logger.info("The image=(%s) is being deleted from the OpenStack cluster." % image.name) glance.images.delete(image.osid) except Exception as ex: # image.osid didn't exist, or image wasn't installed pass # zero this image on the appliance image.osid = "" image.active = 0 image.update() # response response['response'] = "error" response['result']['image'] = row2dict(image) response['result']['message'] = "Failed to install image into the cluster." app.logger.error("Failed to install image=(%s) into the OpenStack cluster." % (image.name)) else: # install success! update updated time for image pattern = '%Y-%m-%dT%H:%M:%S' image.updated = int(time.mktime(time.strptime(osimage.created_at, pattern))) image.osid = osimage.id image.update() # response response['response'] = "success" response['result']['image'] = row2dict(image) response['result']['message'] = "Image installed." return response
def flavor_verify_install(flavor): # build the response response = {"response": "", "result": {"message": "", "flavor": {}}} # get the cluster configuration try: openstack = db.session.query(OpenStack).first() # what happens if they haven't configured it already? if not openstack: raise OpenStackConfiguration("OpenStack configuration isn't complete.") except Exception as ex: # return error flavor_error_response(ex) # establish connection to openstack try: nova = nova_connection() except Exception as ex: # return error flavor_error_response(ex) # look up flavors try: targetflavor = None # look up the flavor by name and stop on it osflavors = nova.flavors.list() for osflavor in osflavors: if osflavor.name == flavor.name: targetflavor = osflavor break except: # no flavor found targetflavor = None # check for install needed install_flavor = False # check flavor specs match if targetflavor: if targetflavor.vcpus != flavor.vpus: # vpus wrong install_flavor = True if targetflavor.disk != flavor.disk: # disk size wrong install_flavor = True if targetflavor.ram != flavor.memory: # memory wrong install_flavor = True # get the flavor network quota keys (if required) try: if flavor.network > 0: # get the flavor keys from openstack # throws not found if they don't exist osikeys = targetflavor.get_keys() # check quotas match if 'quota:inbound_average' in osikeys and 'quota:outbound_average' in osikeys: if flavor.network != int(osikeys['quota:inbound_average']): install_flavor = True if flavor.network != int(osikeys['quota:outbound_average']): install_flavor = True else: install_flavor = True else: # do nothing pass except: # just force install install_flavor = True else: # no flavor found install_flavor = True app.logger.info("Flavor not found.") # install the flavor if install_flavor: if targetflavor: try: # remove the old flavor nova.flavors.delete(targetflavor.id) except: app.logger.info("Could not remove the old flavor=(%s) from the OpenStack cluster." % flavor.name) # referenced from ticket #80 # create the new flavor targetflavor = nova.flavors.create( flavor.name, flavor.memory, flavor.vpus, flavor.disk, flavorid='auto', ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True ) # set bandwidth targetflavor.set_keys({"quota:inbound_average": flavor.network}) targetflavor.set_keys({"quota:outbound_average": flavor.network}) app.logger.info("Installed flavor=(%s) into the OpenStack cluster." % flavor.name) # update the flavor database with id flavor.osid = targetflavor.id flavor.update(flavor) # response response['response'] = "success" response['result']['message'] = "Flavor added." response['result']['flavor'] = row2dict(flavor) return response
def flavor_verify_install(flavor): # build the response response = {"response": "", "result": {"message": "", "flavor": {}}} # get the cluster configuration try: openstack = db.session.query(OpenStack).first() # what happens if they haven't configured it already? if not openstack: raise OpenStackConfiguration("OpenStack configuration isn't complete.") except Exception as ex: # return error flavor_error_response(ex) # establish connection to openstack try: nova = nova_connection() except Exception as ex: # return error flavor_error_response(ex) # look up flavors try: # look up the flavor by name and stop on it targetflavor = nova.flavors.get(flavor.osid) except: # flavor could not be retreived by osid targetflavor = None osflavors = nova.flavors.list() for osflavor in osflavors: if osflavor.name == flavor.name: if flavor.is_same_as_osflavor(osflavor): targetflavor = osflavor break if not targetflavor: try: # referenced from ticket #80 # create the new flavor targetflavor = nova.flavors.create( flavor.name, flavor.memory, flavor.vpus, flavor.disk, flavorid='auto', ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True ) except nova_exceptions.Forbidden: response['response'] = "forbidden" response['result']['message'] = "Can't install flavor due to lack of permissions for tenant user." return response except Exception as ex: response['response'] = "error" response['result']['message'] = "Error installing flavor inside OpenStack. %s" % ex return response # set bandwidth targetflavor.set_keys({"quota:inbound_average": flavor.network_down}) targetflavor.set_keys({"quota:outbound_average": flavor.network_up}) app.logger.info("Installed flavor=(%s) into the OpenStack cluster." % flavor.name) # update the flavor database with id flavor.update(osid=targetflavor.id, installed=True) # response response['response'] = "success" response['result']['message'] = "Flavor added." response['result']['flavor'] = row2dict(flavor) return response
def configure_flavors_detail(flavor_id): # get the matching flavor flavor = db.session.query(Flavors).filter_by(id=flavor_id).first() # clear settings cache Status().flush() # enable/diskable if 'enable' in request.form.keys(): flavor.update(active=int(request.form['enable'])) # set ask if 'ask' in request.form.keys(): flavor.update(ask=int(request.form['ask'])) # set max-instances if 'max-instances' in request.form.keys(): flavor.update(max_instances=int(request.form['max-instances'])) # install pool flavor if 'install' in request.form.keys(): # let's see what we can break, er install try: if not flavor: response = jsonify({"response": "error", "result": {"message": "Flavor %s not found." % flavor_id }}) response.status_code = 404 return response # we are told to install if int(request.form['install']): response = flavor_verify_install(flavor) if not response['response'] == 'success': raise Exception(response['result']['message']) if flavor.ask > 0: flavor.update(active=True) else: flavor.update(active=False) else: # we are told to uninstall (install=0) response = flavor_uninstall(flavor) if not response['response'] == 'success': raise Exception(response['result']['message']) flavor.update(installed=False, active=False, osid=None) except Exception as e: response = jsonify({"response": "error", "result": {"message": str(e)}}) response.status_code = 403 return response # set instance state to match flavor's state instances = Instances() instances.toggle(flavor.id, flavor.active) # update the ask prices on the openstack cluster using metadata try: # get current ask price on openstack and update flavor.save() # warn because we couldn't update the ask price that's set on openstack except nova_exceptions.Forbidden: app.logger.warning('No permissions to update price of flavor "{0}".'.format( flavor.name)) return response # handle any other exception while talking to openstack except Exception as e: app.logger.warning('Error updating price of flavor "{0}": {1}.'.format( flavor.name, str(e))) return jsonify({"response": "success", "flavor": row2dict(flavor)})