Example #1
0
def message(text=None, status="success", reloader=False):
	from webapp.models.models import Appliance
	apitoken = Appliance().get().apitoken

	# muck reloader string
	reloader = "true" if reloader else "false"

	if app.config['DEBUG'] == True:
		url = "http://0.0.0.0:%s/api/message?text=%s&status=%s&reload=%s&apitoken=%s" % (
			app.config['DEV_PORT'],
			quote_plus(text),
			status,
			reloader,
			apitoken
		)
	else:
		url = "http://0.0.0.0:80/api/message?text=%s&status=%s&reload=%s&apitoken=%s" % (
			quote_plus(text),
			status,
			reloader,
			apitoken
		)

	try:
		response = urlopen(url, data="", timeout=10).read()
		return True
	except Exception, e:
		return False
Example #2
0
    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
Example #3
0
    def action(ip=('i', default_ip)):
        """
		Installs a new database configuration for the appliance.
		"""

        # create all tables
        db.create_all()

        if not Appliance.get():
            # initialize the appliance object
            appliance = Appliance()
            appliance.initialize(ip)

        # sync flavors from pool (openstack sync comes later when we have a user)
        flavors = Flavors().sync()

        # configure output
        configure_blurb()
Example #4
0
	def action(ip=('i', default_ip)):
		"""
		Installs a new database configuration for the appliance.
		"""

		# create all tables
		db.create_all()
		
		if not Appliance.get():
			# initialize the appliance object
			appliance = Appliance()
			appliance.initialize(ip)

		# sync flavors from pool (openstack sync comes later when we have a user)
		flavors = Flavors().sync()

		# configure output
		configure_blurb()
Example #5
0
    def action(ip=('i', default_ip)):
        """
		Restores the appliance to factory default settings.
		"""
        try:
            if ip == default_ip:
                print "Please enter the appliance's IP address."
                print "Usage: ./manage.py reset -i x.x.x.x"
                return action

            # double check they want to do this
            if query_yes_no("Are you sure you want to reset the appliance?"):

                # initialize database
                path = os.path.dirname(os.path.abspath(__file__))
                os.system('sqlite3 "%s/utterio.db" < "%s/schema.sql"' %
                          (path, path))

                # initialize the appliance object
                appliance = Appliance()
                appliance.initialize(ip)

                # sync with pool database
                images = Images()
                iresponse = images.sync(appliance)

                flavors = Flavors()
                fresponse = flavors.sync(appliance)

                if iresponse['response'] != "success":
                    print iresponse['result']
                elif fresponse['response'] != "success":
                    print iresponse['result']
                else:
                    print "The database has been cleared and a new API token has been generated."
                    configure_blurb()

        except ValueError as ex:
            print ex
Example #6
0
	def action(ip=('i', default_ip)):
		"""
		Restores the appliance to factory default settings.
		"""
		try:
			if ip == default_ip:
				print "Please enter the appliance's IP address."
				print "Usage: ./manage.py reset -i x.x.x.x"
				return action

			# double check they want to do this	
			if query_yes_no("Are you sure you want to reset the appliance?"):

				# initialize database
				path = os.path.dirname(os.path.abspath(__file__))
				os.system('sqlite3 "%s/utterio.db" < "%s/schema.sql"' % (path, path))

				# initialize the appliance object
				appliance = Appliance()
				appliance.initialize(ip)

				# sync with pool database
				images = Images()
				iresponse = images.sync(appliance)

				flavors = Flavors()
				fresponse = flavors.sync(appliance)

				if iresponse['response'] != "success":
					print iresponse['result']
				elif fresponse['response'] != "success":
					print iresponse['result']
				else:
					print "The database has been cleared and a new API token has been generated."
					configure_blurb()

		except ValueError as ex:
			print ex
Example #7
0
	def action(ip=('i', default_ip)):
		"""
		Installs a new database configuration for the appliance.
		"""
		# run database reset script - use current path to run file
		path = os.path.dirname(os.path.abspath(__file__))

		# initialize database
		os.system('sqlite3 "%s/utterio.db" < "%s/schema.sql"' % (path, path))
		
		# initialize the appliance object
		appliance = Appliance()
		appliance.initialize(ip)
		
		# sync to remote database
		images = Images()
		response = images.sync(appliance)

		flavors = Flavors()
		response = flavors.sync(appliance)

		# configure output
		configure_blurb()
Example #8
0
    def action(ip=('i', default_ip)):
        """
		Installs a new database configuration for the appliance.
		"""
        # run database reset script - use current path to run file
        path = os.path.dirname(os.path.abspath(__file__))

        # initialize database
        os.system('sqlite3 "%s/utterio.db" < "%s/schema.sql"' % (path, path))

        # initialize the appliance object
        appliance = Appliance()
        appliance.initialize(ip)

        # sync to remote database
        images = Images()
        response = images.sync(appliance)

        flavors = Flavors()
        response = flavors.sync(appliance)

        # configure output
        configure_blurb()
Example #9
0
    def proxy_image(self):
        if Appliance.get().enable_image_caching:
            url = self.cached_url
        else:
            url = self.url
        tmp_file = self.get_data(url)
        osid = create_os_image(name=self.name,
                               disk_format=self.disk_format,
                               container_format=self.container_format,
                               fd=tmp_file).id

        # close tmp file fd, this should delete it from the disk
        tmp_file.close()
        self.update(osid=osid)
Example #10
0
    def action(ip=('i', default_ip)):
        """
		Restores the appliance to factory default settings.
		"""
        try:
            if ip == default_ip:
                print "Please enter the appliance's IP address."
                print "Usage: ./manage.py reset -i x.x.x.x"
                return action

            # double check they want to do this
            if query_yes_no("Are you sure you want to reset the appliance?"):

                # initialize database
                path = os.path.dirname(os.path.abspath(__file__))
                os.system('cp "%s/utterio.db" "%s/utterio_backup.db"' %
                          (path, path))

                # delete, then create all tables
                db.drop_all()
                db.create_all()

                # initialize the appliance object
                appliance = Appliance()
                appliance.initialize(ip)

                # sync with pool database
                flavors = Flavors().sync()

                if flavors['response'] != "success":
                    print flavors['result']
                else:
                    print "The database has been cleared and a new API token has been generated."
                    configure_blurb()

        except ValueError as ex:
            print ex
Example #11
0
def configure_images():
    # handle a GET
    if request.method == "GET":
        # check configuration
        settings = Status().check_settings()

        # load images and appliance
        images = db.session.query(Images).all()
        appliance = Appliance().get()

        return render_template("configure/images.html", settings=settings, appliance=appliance, images=images)

        # handle a PUT
    elif request.method == "PUT":
        # clear settings cache
        Status().flush()

        # load appliance object
        appliance = Appliance().get()

        try:
            state = request.form["dynamicimages"]
            appliance.dynamicimages = state

            # update entry
            appliance.update()

            if state == 0:
                state_out = "disabled"
            else:
                state_out = "enabled"

            response = {"response": "success", "result": "dynamicimages %s" % state_out}
        except:
            response = {"response": "error", "result": "no valid parameters supplied"}

        return jsonify(response)
Example #12
0
	def action(ip=('i', default_ip)):
		"""
		Restores the appliance to factory default settings.
		"""
		try:
			if ip == default_ip:
				print "Please enter the appliance's IP address."
				print "Usage: ./manage.py reset -i x.x.x.x"
				return action

			# double check they want to do this	
			if query_yes_no("Are you sure you want to reset the appliance?"):

				# initialize database
				path = os.path.dirname(os.path.abspath(__file__))
				os.system('cp "%s/utterio.db" "%s/utterio_backup.db"' % (path, path))

				# delete, then create all tables
				db.drop_all()
				db.create_all()

				# initialize the appliance object
				appliance = Appliance()
				appliance.initialize(ip)

				# sync with pool database
				flavors = Flavors().sync()

				if flavors['response'] != "success":
					print flavors['result']
				else:
					print "The database has been cleared and a new API token has been generated."
					configure_blurb()

		except ValueError as ex:
			print ex
Example #13
0
	def proxy_image(self):
		if Appliance.get().enable_image_caching:
			url = self.cached_url
		else:
			url = self.url
		tmp_file = self.get_data(url)
		osid = create_os_image(
			name=self.name,
			disk_format=self.disk_format,
			container_format=self.container_format,
			fd=tmp_file).id

		# close tmp file fd, this should delete it from the disk
		tmp_file.close()
		self.update(osid=osid)
Example #14
0
	def cached_url(self):
		# resolve url to it's final destination
		urlinfo = urllib2.build_opener().open(urllib2.Request(self.url))

		# remove protocol from url
		proto_search = re.compile('^(http|https)://(.*)$').search(urlinfo.url)

		# clean up the open connection
		urlinfo.close()

		if proto_search:
			proto = proto_search.group(1)
			host_path = proto_search.group(2)
			url = 'http://{0}:8080/{1}/{2}'.format(Appliance.get().local_ip, proto, host_path)
		else:
			url = self.url
		return url
Example #15
0
    def cached_url(self):
        # resolve url to it's final destination
        urlinfo = urllib2.build_opener().open(urllib2.Request(self.url))

        # remove protocol from url
        proto_search = re.compile('^(http|https)://(.*)$').search(urlinfo.url)

        # clean up the open connection
        urlinfo.close()

        if proto_search:
            proto = proto_search.group(1)
            host_path = proto_search.group(2)
            url = 'http://{0}:8080/{1}/{2}'.format(Appliance.get().local_ip,
                                                   proto, host_path)
        else:
            url = self.url
        return url
Example #16
0
def configure_images():
    # handle a GET
    if request.method == 'GET':
        # check configuration
        settings = Status().check_settings()

        # load images and appliance
        images = db.session.query(Images).all()
        appliance = Appliance().get()

        return render_template('configure/images.html',
                               settings=settings,
                               appliance=appliance,
                               images=images)

    # handle a PUT
    elif request.method == 'PUT':
        # clear settings cache
        Status().flush()

        # load appliance object
        appliance = Appliance().get()

        try:
            state = request.form['dynamicimages']
            appliance.dynamicimages = state

            # update entry
            appliance.update()

            if state == 0:
                state_out = "disabled"
            else:
                state_out = "enabled"

            response = {
                "response": "success",
                "result": "dynamicimages %s" % state_out
            }
        except:
            response = {
                "response": "error",
                "result": "no valid parameters supplied"
            }

        return jsonify(response)
Example #17
0
	def save(self, *args, **kwargs):
		if Appliance.get().enable_image_caching:
			url = self.cached_url
		else:
			url = self.url
		if (self.osid == None or not os_image_exists(self.osid)):
			if self.decompress:
				app.logger.info("Proxying image in order to decompress {0}.".format(
					url))
				self.proxy_image()
			else:
				app.logger.info("Creating image with location {0}.".format(
					url))
				self.osid = create_os_image(
					name=self.name,
					url=url,
					disk_format=self.disk_format,
					container_format=self.container_format).id
		super(Images, self).save(*args, **kwargs)
Example #18
0
 def save(self, *args, **kwargs):
     if Appliance.get().enable_image_caching:
         url = self.cached_url
     else:
         url = self.url
     if (self.osid == None or not os_image_exists(self.osid)):
         if self.decompress:
             app.logger.info(
                 "Proxying image in order to decompress {0}.".format(url))
             self.proxy_image()
         else:
             app.logger.info(
                 "Creating image with location {0}.".format(url))
             self.osid = create_os_image(
                 name=self.name,
                 url=url,
                 disk_format=self.disk_format,
                 container_format=self.container_format).id
     super(Images, self).save(*args, **kwargs)
Example #19
0
    def trashman(self):
        from webapp.libs.openstack import instance_info
        from webapp.libs.openstack import instance_decommission

        # build the response
        response = {
            "response": "success",
            "result": {
                "message": "",
                "server": {}
            }
        }

        # get instance (server) info
        cluster_response = instance_info(self)

        if cluster_response['response'] == "success":
            # we should NOT have this, so try to decomission out of desperation
            cluster_response = instance_decommission(self)
            response['result'][
                'message'] = "Terminating instance %s" % self.name
        else:
            # delete this instance into forever
            address = Addresses().get_by_id(self.address_id)
            address.release()
            self.delete(self)
            response['result'][
                'message'] = "Instance %s has been deleted." % self.name

        # make a call to the callback url to report instance details
        appliance = Appliance().get()
        callback_url = self.callback_url
        pool_response = pool_instance(url=callback_url,
                                      instance=self,
                                      appliance=appliance)

        return response
Example #20
0
def configure_flavors():
	# check configuration
	settings = Status().check_settings()

	# flavors without the ones that were synced from pool are not installed on 
	# openstack cluster yet
	flavors = Flavors.query.filter_by(installed=True).all()

	# load appliance
	appliance = Appliance.get()

	# how much is BTC?
	try:
		quote = float(coinbase_get_quote(currency='btc_to_usd')['result']['btc_to_usd'])/1000000
	except:
		quote = 0

	return render_template(
		'configure/flavors.html',
		settings=settings,
		quote=quote,
		flavors=flavors,
		appliance=appliance
	)
Example #21
0
def configure_flavors():
    # check configuration
    settings = Status().check_settings()

    # flavors without the ones that were synced from pool are not installed on
    # openstack cluster yet
    flavors = Flavors.query.filter_by(installed=True).all()

    # load appliance
    appliance = Appliance.get()

    # how much is BTC?
    try:
        quote = float(
            coinbase_get_quote(
                currency='btc_to_usd')['result']['btc_to_usd']) / 1000000
    except:
        quote = 0

    return render_template('configure/flavors.html',
                           settings=settings,
                           quote=quote,
                           flavors=flavors,
                           appliance=appliance)
Example #22
0
    def nudge(self):
        from webapp.libs.openstack import try_associate_floating_ip
        from webapp.libs.openstack import instance_info
        from webapp.libs.openstack import instance_console
        from webapp.libs.openstack import instance_decommission

        # get instance console output
        response = instance_console(self)
        if 'console' in response['result']:
            self.console = response['result']['console']
        self.update()

        # get instance (server) info
        response = instance_info(self)

        # set start state
        start_state = self.state

        # set instance meta data
        if response['response'] == "success":
            server = response['result']['server']

            # if the state is ACTIVE, we set to be running state==4
            if server.status == "ACTIVE":
                # set network info
                self.state = 4

                # try to get a floating ip for the new server
                float_response = try_associate_floating_ip(server)

                # check if call got a floating IP
                if float_response['response'] == "success":
                    # get instance info again to pick up new IP
                    response = instance_info(self)

                    # load the response into the server object
                    if response['response'] == "success":
                        server = response['result']['server']

                else:
                    # log 'errors' in floating assignment
                    app.logger.info(float_response['result']['message'])

                # extract IP addresses using IPy
                # in some circumstances this will squash multiple same/same address types
                # we only extract and store one each of private ipv4, public ipv4, and public ipv6
                for key in server.networks.keys():  # any network names
                    for address in server.networks[
                            key]:  # loop through each address for each network
                        # private IPv4
                        if IP(address).iptype() == "PRIVATE" and IP(
                                address).version() == 4:
                            self.privateipv4 = address
                        # public IPv4
                        elif IP(address).iptype() == "PUBLIC" and IP(
                                address).version() == 4:
                            self.publicipv4 = address
                        # public IPv6
                        elif IP(address).iptype() == "ALLOCATED ARIN" and IP(
                                address).version() == 6:
                            self.publicipv6 = address

                # update the instance
                self.update()

            # ERROR status from openstack
            elif server.status == "ERROR":
                # instance failed to start, so delete and reset to paid
                response = instance_decommission(self)

                self.state = 2  # will be started again shortly
                self.update()

                response['response'] = "error"
                response['result'][
                    'message'] = "OpenStack errored on instance start."

                app.logger.error(
                    "OpenStack error on starting instance=(%s).  Setting to restart."
                    % self.name)

            # SPAWNING status from openstack
            else:
                # we all have limited time in this reality
                epoch_time = int(time.time())

                # wait_timer is 5 minutes after the last update
                wait_timer = self.updated + 300

                # test to see if we are 'hung' on SPAWNING for more than wait_timer
                if epoch_time > wait_timer:
                    # we're now  past when the instance needed to move to RUNNING
                    response = instance_decommission(self)

                    response['response'] = "error"
                    response['result'][
                        'message'] = "Setting instance %s to restart." % self.name

                    self.state = 2  # will be started shortly after this by start
                    self.updated = epoch_time  # given we 'timed' out, give the instance more time
                    self.update()
                    """
					of anyplace, this is where you *might* want to add some time to the instance
					because a time based payment has been made on it.  however, this could lead to 
					a situation where an instance gets stuck in a circular state of erroring, getting
					more time, erroring again, rinse and repeat.  instead of embracing this eventuality, 
					we choose to short the customer her measly few cents instead, and let it serve as a 
					as an excuse to add 'karma hits' on bad starts from providers as a feature later
					"""

                    app.logger.error(
                        "OpenStack hung starting instance=(%s).  Setting to restart."
                        % self.name)

                else:
                    # this is a 'soft' fail
                    response['response'] = "error"
                    response['result'][
                        'message'] = "Still starting instance=(%s)." % self.name

        # OpenStack reports instance NOT FOUND
        else:
            # we all have limited time in this reality
            epoch_time = int(time.time())

            # we first check if we're outright expired (shouldn't happen)
            if self.expires < epoch_time:
                # no reason to be running as we're expired
                self.state = 7  # will be deleted shortly after this by trashman
                self.update()

                response['response'] = "error"
                response['result'][
                    'message'] = "Instance %s decommissioned." % self.name

                app.logger.error(
                    "OpenStack couldn't find expired instance=(%s). Decomissioning."
                    % self.name)

            else:
                # we didn't find the instance in openstack, yet we should be running
                self.state = 2  # set to be started again
                self.update()

                response['response'] = "error"
                response['result'][
                    'message'] = "OpenStack couldn't find instance.  Restarting."

                app.logger.error(
                    "OpenStack couldn't find instance=(%s). Setting to restart."
                    % self.name)

        # make a call to the callback url to report instance details on state change
        if self.state != start_state:
            appliance = Appliance().get()
            callback_url = self.callback_url
            pool_response = pool_instances(url=callback_url,
                                           instance=self,
                                           appliance=appliance)

        return response
Example #23
0
	def appliance(self):
		return Appliance.get()
Example #24
0
 def appliance(self):
     return Appliance.get()
Example #25
0
    def housekeeping(self):
        from webapp.libs.openstack import instance_info
        from webapp.libs.openstack import instance_decommission
        from webapp.libs.openstack import instance_console

        # build the response
        response = {
            "response": "success",
            "result": {
                "message": "",
                "server": {}
            }
        }

        # get instance (server) info
        cluster_response = instance_info(self)
        server = cluster_response['result']['server']

        # we all have limited time in this reality
        epoch_time = int(time.time())

        # set start state
        start_state = self.state

        # this is complicated...because we aren't EC with OpenStack...or I'm crazy
        if cluster_response['response'] == "success":
            # openstack responded it found this instance
            if server.status == "ACTIVE":
                # openstack says the server is running
                if self.expires < epoch_time:
                    # suspend the instance for non-payment
                    try:
                        self.suspend()
                        response['result'][
                            'message'] = "Instance %s suspended." % self.name
                    except Exception as e:
                        response['response'] = 'error'
                        response['result']['message'] = \
                         'Instance {instance} suspending failed: "{error}".'.format(
                          instance=self.name, error=str(e))
                        return response
                    self.state = 5
                elif self.expires > epoch_time:
                    # openstack says we're running, and we're paid
                    if self.state == 5 or self.state == 6:
                        # we move the instance to starting mode
                        response['result'][
                            'message'] = "Instance %s is starting." % self.name
                        self.state = 3
            elif server.status == "SUSPENDED" or server.status == "SHUTOFF":
                # openstack says this instance is suspended
                if self.expires > epoch_time:
                    # should be running because not expired
                    try:
                        self.resume()
                        response['result'][
                            'message'] = "Instance %s resumed." % self.name
                    except Exception as e:
                        response['response'] = 'error'
                        response['result']['message'] = \
                         'Instance {instance} resume failed: "{error}".'.format(
                          instance=self.name, error=str(e))
                        return response
                    self.state = 3  # mark as starting
                if self.expires + app.config[
                        'POOL_DECOMMISSION_TIME'] < epoch_time:
                    # should be destroyed (suspended for +2 hours without pay)
                    response['result'][
                        'message'] = "Instance %s decommissioned." % self.name
                    self.state = 7
            else:
                # openstack indicates another state besides SUSPENDED or ACTIVE
                if self.expires > epoch_time:
                    # we should be running, but in a weird state - destroy then restart
                    response = instance_decommission(self)
                    response['result'][
                        'message'] = "Instance %s restarted." % self.name
                    self.state = 2  # set as paid and ready to start
                    app.logger.error(
                        "OpenStack says instance=(%s) isn't in the correct state.  Setting to restart."
                        % self.name)
                else:
                    # expired but in a weird state - destroy
                    response = instance_decommission(self)
                    response['result'][
                        'message'] = "Instance %s decommissioned." % self.name
                    self.state = 7
        else:
            # openstack can't find this instance
            if self.expires > epoch_time:
                if self.state == 2:
                    # check error rate
                    if self.message_count > 10:
                        # we're failing to start the instance, so decomission
                        response['result'][
                            'message'] = "Instance %s decommissioned." % self.name
                        self.state = 7
                        app.logger.error(
                            "Exceeded error rate on callbacks for instance=(%s). Decomissioning."
                            % self.name)
                else:
                    # set instance to restart - not expired, should be running
                    response[
                        'response'] = "error"  # technically, someone is probably f*****g with things
                    response['result'][
                        'message'] = "Setting instance %s to restart." % self.name
                    self.state = 2  # will be started shortly after this by start
                    app.logger.error(
                        "OpenStack doesn't know about instance=(%s). Setting to restart."
                        % self.name)
            else:
                # no reason to be running
                response['response'] = "error"
                response['result'][
                    'message'] = "Instance %s decommissioned." % self.name
                self.state = 7  # will be deleted shortly after this by trashman

        # get instance console output
        cluster_response = instance_console(self)
        if 'console' in response['result']:
            self.console = response['result']['console']

        # save updated properties
        self.save()

        # make a call to the callback url to report instance details if either the
        # state has changed or the last state change is less than 900 secs ago
        if self.state != start_state or self.updated >= int(time.time()) - 900:
            appliance = Appliance().get()
            callback_url = self.callback_url
            pool_response = pool_instances(url=callback_url,
                                           instance=self,
                                           appliance=appliance)

        return response
Example #26
0
    def start(self):
        from webapp.libs.openstack import flavor_verify_install
        from webapp.libs.openstack import instance_start

        # build the response
        response = {"response": "success", "result": {"message": ""}}

        # appliance
        appliance = Appliance().get()

        # load the callback url (expected to be None first time through)
        callback_url = self.callback_url

        # check if instance needs to reset
        epoch_time = int(time.time())
        if self.expires < epoch_time:
            # instance time expired, so don't start
            self.state = 1
            self.update()
            response['response'] = "error"
            response['result'][
                'message'] = "Instance payment is expired.  Now waiting on payment."

        # we run a maximum of 7 callback checks
        for loop_count in range(7):
            # set state to 3 just for the pool
            self.state = 3
            # make a call to the callback url to get instance details
            pool_response = pool_instances(url=callback_url,
                                           instance=self,
                                           appliance=appliance)
            # and set it back to 2 because we don't know yet if it's actually starting
            self.state = 2

            # check for a failure to contact the callback server
            if pool_response['response'] == "error":
                self.message = pool_response['result']['message']
                self.message_count = self.message_count + 1
                self.update()
                return pool_response

            # look and see if we have a callback_url in the response
            try:
                # run the loop again to call the callback url
                if pool_response['result']['instance']['callback_url'] == '':
                    break
                else:
                    callback_url = pool_response['result']['instance'][
                        'callback_url']
            except:
                # catch no callback_url keys
                break
        else:
            response['response'] = "error"
            response['result']['message'] = "Callback depth exceeded."
            self.message = response['result']['message']
            self.message_count = self.message_count + 1
            self.update()
            return response

        # get dictionary from pool's reply
        start_params = schemas['InstanceStartParametersSchema'](
            **pool_response['result']['instance']).as_dict()

        # and lo, callback_url is saved
        self.callback_url = callback_url

        # lookup the image for this instance, or create it otherwise
        image = self.image
        if not image:
            image = Images.query.filter_by(**dict(
                filter(
                    lambda x: x[
                        0] in ['url', 'container_format', 'disk_format'],
                    start_params['image'].items()))).first()
            self.update(image=image)
        if image and not image.osid:
            image.delete()
            image = None
        if not image:
            image = Images(**start_params['image'])
            self.update(image=image)
            try:
                image.save()
            except Exception as e:
                app.logger.warning(
                    "Error creating image using copy_from, attempt proxying: \"{0}\""
                    .format(str(e)))
                response = self._proxy_image(image)
                if response['response'] != 'success':
                    return response

        # if image is not ready because it's either killed or still downloading
        try:
            image_status = image.status
        except Exception as e:
            err_string = "Error communicating with OpenStack: \"{0}\"".format(
                str(e))
            app.logger.error(err_string)
            response['response'] = "error"
            response['result']['message'] = err_string
            return response
        if image_status == "queued" or image_status == "saving":
            # image is still downloading and is not ready to be used yet
            response['response'] = "queued"
            response['result']['message'] = "image is being created"
            return response
        elif image_status == "killed":
            # image has been killed, prossibly our openstack is a nebula
            response = self._proxy_image(image)
            if response['response'] != 'success':
                return response

        # post creation file is blank to start
        post_creation_combo = ""

        # load the parser to unencode jinja2 template escaping from appliance
        h = HTMLParser()

        # ssh_key unrolling
        try:
            # loop through both strings and cat onto post_creation_ssh_key_combo
            # using prefered method of injecting keys with cloud-init
            post_creation_combo += "#cloud-config\n"
            post_creation_combo += "ssh_authorized_keys:\n"
            for line in start_params['ssh_keys']:
                post_creation_combo += " - %s\n" % h.unescape(line)
            post_creation_combo += "\n"

        except:
            # do nothing on various key failure
            pass

        # create utterio file data
        post_creation_file_data = ""
        post_creation_file_data += "export MY_BITCOIN_ADDRESS=%s\n" % self.address
        post_creation_file_data += "export MY_POOL_API_ADDRESS=%s/api/v1/instances/%s/\n" % (
            app.config['APP_WEBSITE'], self.name)

        # payment address source file
        post_creation_combo += "write_files:\n"
        post_creation_combo += "- encoding: b64\n"
        post_creation_combo += '  content: %s\n' % base64.b64encode(
            post_creation_file_data)
        post_creation_combo += "  path: /etc/utterio\n"
        post_creation_combo += "  permissions: '0644'\n"

        # post creation configuration handling
        try:
            for line in start_params['post_create']:
                # import what the user put in the textbox for their wisp
                post_creation_combo += "%s\n" % h.unescape(line)

            post_creation_combo += "\n"

        except:
            # do nothing on post creation failure
            pass

        # update the instance with post creation
        self.post_creation = post_creation_combo

        self.update()

        # take the instance's flavor and verify install
        flavor = models.flavors.Flavors().get_by_id(self.flavor.id)
        flavor_response = flavor_verify_install(flavor)

        if flavor_response['response'] == "error" or flavor_response[
                'response'] == "forbidden":
            # we've failed to install flavor, so we disable it
            flavor.osid = ""
            flavor.active = 0
            flavor.update()

            # now we disable the other instances using the flavor
            instances = Instances()
            instances.toggle(flavor.id, 0)

            # disable this instance
            self.state = 0
            self.expires = self.created  # zeros out the payment
            self.update()

            if flavor_response['response'] == "forbidden":
                response['result']['message'] = \
                 "Not allowed to create flavor inside OpenStack."

                # log it
                app.logger.error(
                    "Disabling all instances using flavor=(%s) and disabling "
                    "creation of flavors due to lack of permissions." %
                    flavor.name)

            else:
                # log it
                app.logger.error(
                    "Disabling all instances using flavor=(%s) due to "
                    "OpenStack failure." % flavor.name)

                # build the response and return
                response['result']['message'] = flavor_response['result'][
                    'message']

            response['response'] = "error"
            return response

        # tell openstack to start the instance
        cluster_response = instance_start(self)

        # process response
        if cluster_response['response'] == "success":
            server = cluster_response['result']['server']
            self.update(osid=server.id, state=3)
            response['result'] = cluster_response['result']
        else:
            response = cluster_response

        return response
Example #27
0
    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
Example #28
0
    def start(self):
        from webapp.libs.openstack import flavor_verify_install
        from webapp.libs.openstack import image_verify_install
        from webapp.libs.openstack import instance_start

        # build the response
        response = {"response": "success", "result": {"message": ""}}

        # appliance
        appliance = Appliance().get()

        # load the callback url (expected to be None)
        callback_url = self.callback_url

        # check if instance needs to reset
        epoch_time = int(time.time())
        if self.expires < epoch_time:
            # instance time expired, so don't start
            self.state = 1
            self.update()
            response['response'] = "error"
            response['result'][
                'message'] = "Instance payment is expired.  Now waiting on payment."

        # we run a maximum of 7 callback checks
        for loop_count in range(7):
            # make a call to the callback url to get instance details
            next_state = 3  # hack the expected next state into the pool packet
            pool_response = pool_instance(url=callback_url,
                                          instance=self,
                                          next_state=next_state,
                                          appliance=appliance)

            # check for a failure to contact the callback server
            if pool_response['response'] == "error":
                self.message = pool_response['result']['message']
                self.message_count = self.message_count + 1
                self.update()
                return pool_response

            # look and see if we have a callback_url in the response
            try:
                callback_url = pool_response['result']['instance'][
                    'callback_url']
                # run the loop again to call the callback url
                continue
            except:
                # break out
                break

        # for else returns a depth error
        else:
            response['response'] = "error"
            response['result']['message'] = "Callback depth exceeded."
            self.message = response['result']['message']
            self.message_count = self.message_count + 1
            self.update()
            return response

        # and lo, callback_url is saved
        self.callback_url = callback_url
        self.update()

        # get the image name if it exists in the response
        try:
            image_name = pool_response['result']['instance']['image']
            image = db.session.query(Images).filter_by(name=image_name).first()
            self.image_id = image.id
            self.update()
        except:
            image_name = None
            # get the dynamic image url if it exists in the response
            try:
                dynamic_image_url = pool_response['result']['instance'][
                    'dynamic_image_url']
                self.dynamic_image_url = dynamic_image_url
                self.update()
            except:
                # not good, but we can use a default
                image = db.session.query(Images).first()
                self.image_id = image.id
                self.update()

        # post creation file is blank to start
        post_creation_ssh_key_combo = ""

        # load the parser to unencode jinja2 template escaping from appliance
        h = HTMLParser()

        # ssh_key unrolling
        try:
            ssh_key = pool_response['result']['instance'][
                'ssh_key']  # an array

            # loop through both strings and cat onto post_creation_ssh_key_combo
            # using prefered method of injecting keys with cloud-init
            post_creation_ssh_key_combo += "#cloud-config\n"
            post_creation_ssh_key_combo += "ssh_authorized_keys:\n"
            for line in ssh_key:
                post_creation_ssh_key_combo += " - %s\n" % h.unescape(line)
            post_creation_ssh_key_combo += "\n"

        except:
            # do nothing on various key failure
            pass

        # post creation configuration handling
        try:
            post_creation = pool_response['result']['instance'][
                'post_creation']  # an array

            for line in post_creation:
                # import what the user put in the textbox for their wisp
                post_creation_ssh_key_combo += "%s\n" % h.unescape(line)

        except:
            # do nothing on post creation failure
            pass

        # update the instance with post creation
        self.post_creation = post_creation_ssh_key_combo
        self.update()

        # take the instance's flavor and verify install
        flavor = Flavors().get_by_id(self.flavor.id)
        osflavor = flavor_verify_install(flavor)

        if osflavor['response'] == "error":
            # we've failed to install flavor, so we disable it
            flavor.osid = ""
            flavor.active = 0
            flavor.update()

            # now we disable the other instances using the flavor
            instances = Instances()
            instances.toggle(flavor.id, 0)

            # disable this instance
            self.state = 0
            self.expires = self.created  # zeros out the payment
            self.update()

            # log it
            app.logger.error(
                "Disabling all instances using flavor=(%s) due to OpenStack failure."
                % flavor.name)

            # build the response and return
            response['response'] = "error"
            response['result'][
                'message'] = "Error creating flavor inside OpenStack."
            return response

        # deal with creating dynamic image or use predefined one
        if self.dynamic_image_url:
            image = Images().get_or_create_by_instance(self)
        else:
            image = Images().get_by_id(self.image.id)

        if not image:
            response['response'] = "error"
            response['result']['message'] = "Error creating dynamic image."
            return response
        else:
            self.image = image
            self.update()

        # take the image and verify install
        osimage = image_verify_install(self.image)

        # handle failures of either flavor or image
        if osimage['response'] == "error":
            response['response'] = "error"
            response['result']['message'] = "Error creating image."
            return response

        # tell openstack to start the instance
        cluster_response = instance_start(self)

        # process response
        if cluster_response['response'] == "success":
            server = cluster_response['result']['server']
            self.osid = server.id  # assign openstack instance id
            self.state = 3  # mark as starting
            self.update()
            response['result'] = cluster_response['result']
        else:
            response = cluster_response

        return response