Esempio n. 1
0
    def get(self):
        # lookup user's auth info
        user_info = User.get_by_id(long(self.user_id))

        # look up instances, then patch them for hotstart support
        pinstances = []
        instances = Instance.get_all()
        for instance in instances:
            try:
                username_patch = instance.user.get().username
            except:
                username_patch = "#HOTSTART#"  # no self

            pinstance = {
                "name": instance.name,
                "status": instance.status,
                "username_patch": username_patch,
                "created": instance.created,
                "expires": instance.expires,
                "key": instance.key
            }
            pinstances.append(pinstance)

        params = {'instances': pinstances}

        return self.render_template('admin/instances.html', **params)
Esempio n. 2
0
    def get(self, name=None):
        # check token
        token = self.request.get('token')
        if token != "":
            user_info = User.get_by_token(token)

            if user_info:
                db_instances = Instance.get_all()

                # work around index warning/errors using a .filter() in models.py
                instances = []
                for db_instance in db_instances:
                    # limit to instances the user has started
                    if db_instance.user == user_info.key:
                        instances.append(db_instance)
                params = {
                    'user_name': user_info.username,
                    'instances': instances
                }

                self.response.headers['Content-Type'] = "application/json"
                return self.render_template('api/instances.json', **params)

        # no token, no user, no data
        params = {
            "response": "fail",
            "message": "must include [token] parameter with a valid token"
        }

        self.response.status = '402 Payment Required'
        self.response.status_int = 402
        self.response.headers['Content-Type'] = "application/json"
        return self.render_template('api/response.json', **params)
Esempio n. 3
0
    def get(self):
        db_instances = Instance.get_all()

        # work around index warning/errors using a .filter() in models.py
        statuses = []
        for db_instance in db_instances:
            # limit to instances the user has started
            statuses.append(db_instance.status[0])

        params = {'statuses': statuses}

        self.response.headers['Content-Type'] = "application/json"
        return self.render_template('api/instances.csv', **params)
Esempio n. 4
0
    def get(self, sid=None):
        # lookup user's auth info
        user_info = User.get_by_id(long(self.user_id))

        # redirect to a POST if we have a sid in the URL
        if sid and user_info.email:
            return self.post(sid)
        try:
            if not user_info.email or not user_info.name or not user_info.company:
                need_more_info = True
            else:
                need_more_info = False
        except:
            need_more_info = True

        # look up user's instances
        db_instances = Instance.get_all()

        # work around index warning/errors using a .filter() in models.py
        instances = []
        for db_instance in db_instances:
            # limit to instances the user has started
            if db_instance.user == user_info.key:
                if db_instance.renamed == None:
                    db_instance.renamed = ""
                instances.append(db_instance)

        streams = Stream.get_all()

        params = {
            'instances': instances,
            'num_instances': len(instances),
            'streams': streams,
            'user_id': self.user_id,
            'user_info': user_info,
            'sid': sid,
            'need_more_info': need_more_info
        }

        return self.render_template('instance/list.html', **params)
Esempio n. 5
0
    def post(self, sid=None):  # a POST here is a create instance event
        # know the user
        user_info = User.get_by_id(long(self.user_id))

        if sid and user_info.email:

            # get form values
            stream = Stream.get_by_sid(sid)

            try:
                size = stream.instance_size
            except:
                size = 0

            # look up user's instances
            db_instances = Instance.get_all()

            # check the user's limits
            instance_count = 0
            for db_instance in db_instances:
                # limit to instances the user has started
                if db_instance.user == user_info.key:
                    instance_count = instance_count + 1

            # warn and redirect if limit is reached
            if (instance_count + 1) > user_info.max_instances:
                self.add_message(
                    'Instance limit reached. This account may only start %s instances. Please delete an existing instance to start a new one!'
                    % user_info.max_instances, 'warning')
                return self.redirect_to('instances-list')

            ## HOT START
            # check for a hot start
            instances = Instance.get_hotstarts()

            for instance in instances:
                # fiveminutesago depends on number of seconds at end of this     ***
                fiveminutesago = datetime.datetime.now() - datetime.timedelta(
                    0, 900)

                # if this hotstart instance has a matching sid, assign and redirect to it
                if instance.created < fiveminutesago and instance.stream.get(
                ).sid == stream.sid and instance.status == "RUNNING":
                    # map to user
                    instance.user = user_info.key
                    instance.hotstart = False
                    instance.put()

                    self.add_message(
                        'Instance assigned! Use login buttons to access %s.' %
                        stream.name, 'success')
                    slack.slack_message("Instance type %s assigned for %s!" %
                                        (stream.name, user_info.username))
                    return self.redirect_to('instance-detail',
                                            name=instance.name)
            #
            ## TRATS TOH

            # make the instance call handle
            http = httplib2.Http(timeout=10)

            # where and who created it (labels for google cloud console)
            if config.isdev:
                iuser = "******" % ("dev", user_info.username.lower())
            else:
                iuser = "******" % ("prod", user_info.username.lower())

            # build url to create new instance from stream
            url = '%s/api/stream/%s?token=%s&user=%s&size=%s' % (
                config.fastener_host_url, sid, config.fastener_api_token,
                iuser, size)

            try:
                # pull the response back TODO add error handling
                response, content = http.request(url, 'POST', None, headers={})
                gcinstance = json.loads(content)
                name = gcinstance['instance']
                password = gcinstance['password']

                if name == "failed":
                    raise Exception("Instance start failed.")

                # set up an instance
                instance = Instance(name=name,
                                    status="PROVISIONING",
                                    user=user_info.key,
                                    stream=stream.key,
                                    size=size,
                                    password=password,
                                    expires=datetime.datetime.now() +
                                    datetime.timedelta(0, 604800),
                                    started=datetime.datetime.now())
                instance.put()

                slack.slack_message("Instance type %s created for %s!" %
                                    (stream.name, user_info.username))

                # give the db a second to update
                if config.isdev:
                    time.sleep(1)

                self.add_message(
                    'Instance created! Grab some coffee and wait for %s to start.'
                    % stream.name, 'success')

                params = {'name': name}
                return self.redirect_to('instance-detail', **params)

            except:
                self.add_message(
                    'The system is currently busy with other instances. Please try again in a few minutes.',
                    'warning')
                return self.redirect_to('instances-list')

        else:
            # email update sumbission
            if not self.form.validate():
                self.add_message(
                    "There were errors validating your email address.",
                    "error")
                return self.get()

            email = self.form.email.data.strip()

            user_info = User.get_by_id(long(self.user_id))
            user_info.email = email.strip()
            user_info.put()

            if len(email) > 3 and not config.isdev:
                name = user_info.name
                try:
                    mc = MarketoClient(config.munchkin_id, config.mclient_id,
                                       config.mclient_secret)
                    try:
                        first = name.split()[0]
                    except:
                        first = ""

                    try:
                        last = name.split()[1]
                    except:
                        last = ""

                    try:
                        company = user_info.company
                    except:
                        company = "None"

                    leads = [{
                        "email": user_info.email,
                        "firstName": first,
                        "lastName": last,
                        "company": company,
                        "leadSource": config.mclient_leadSource
                    }]
                    lead = mc.execute(
                        method='push_lead',
                        leads=leads,
                        lookupField='email',
                        programName=config.mclient_programName,
                        programStatus=config.mclient_programStatus)

                except Exception as ex:
                    slack.slack_message(
                        "Marketo lead create failed because %s." % ex)

            slack.slack_message(
                "We got %s to update their email for instance launch!" %
                user_info.username)

            self.add_message("Thank you! Your email has been updated.",
                             'success')

            # redirect back to GET on list, but with a sid AND email in place this time to create
            if sid:
                return self.redirect_to('streams-start3', sid=sid)
            else:
                return self.redirect_to('instances-list')
Esempio n. 6
0
    def get(self, sid):
        # know the user
        user_info = User.get_by_id(long(self.user_id))

        # check if we have their email
        if not user_info.email:
            self.add_message(
                'Please update your email address before starting an instance!',
                'warning')
            return self.redirect_to('account-settings')

        # look up user's instances
        db_instances = Instance.get_all()

        # check the user's limits
        instance_count = 0
        for db_instance in db_instances:
            # limit to instances the user has started
            if db_instance.user == user_info.key:
                instance_count = instance_count + 1

        # warn and redirect if limit is reached
        if (instance_count + 1) > user_info.max_instances:
            self.add_message(
                'Instance limit reached. This account may only start %s instances. Please delete an existing instance to start a new one!'
                % user_info.max_instances, 'warning')
            return self.redirect_to('instances-list')

        # get stream
        stream = Stream.get_by_sid(sid)

        try:
            size = stream.instance_size
        except:
            size = 0

        ## HOT START
        # check for a hot start
        instances = Instance.get_hotstarts()

        for instance in instances:

            # fiveminutesago depends on number of seconds at end of this     ***
            fiveminutesago = datetime.datetime.now() - datetime.timedelta(
                0, 900)

            # if this hotstart instance has a matching sid, assign and redirect to it
            if instance.created < fiveminutesago and instance.stream.get(
            ).sid == stream.sid and instance.status == "RUNNING":
                # map to user
                instance.user = user_info.key
                instance.hotstart = False
                instance.put()

                self.add_message(
                    'Instance assigned! Use login buttons to access %s.' %
                    stream.name, 'success')
                slack.slack_message("Instance type %s assigned for %s!" %
                                    (stream.name, user_info.username))
                return self.redirect_to('instance-detail', name=instance.name)
        #
        ## TOH TRATS

        # make the instance call to the control box
        http = httplib2.Http(timeout=10)

        # where and who created it
        if config.isdev:
            iuser = "******" % ("dev", user_info.username)
        else:
            iuser = "******" % ("prod", user_info.username)

        url = '%s/api/stream/%s?token=%s&user=%s&size=%s' % (
            config.fastener_host_url, sid, config.fastener_api_token, iuser,
            size)

        try:
            # pull the response back TODO add error handling
            response, content = http.request(url, 'POST', None, headers={})
            gcinstance = json.loads(content)
            name = gcinstance['instance']
            password = gcinstance['password']

            if name == "failed":
                raise Exception("Instance start failed.")

            # set up an instance (note there are two ways to create an instance - see below)
            instance = Instance(name=name,
                                status="PROVISIONING",
                                user=user_info.key,
                                stream=stream.key,
                                size=size,
                                password=password,
                                expires=datetime.datetime.now() +
                                datetime.timedelta(0, 604800),
                                started=datetime.datetime.now())
            instance.put()

            slack.slack_message("Instance type %s created for %s!" %
                                (stream.name, user_info.username))

            # give the db a second to update
            if config.isdev:
                time.sleep(1)

            self.add_message(
                'Instance created! Give the system a few minutes to start %s.'
                % stream.name, 'success')

            params = {'name': name}
            return self.redirect_to('instance-detail', **params)

        except:
            self.add_message(
                'The system is currently busy with other instances. Please try again in a few minutes.',
                'warning')
            return self.redirect_to('instances-list')
Esempio n. 7
0
    def get(self):
        try:
            # grab a list of instances from the Fastener API
            http = httplib2.Http(timeout=30)
            url = '%s/api/instance/list?token=%s' % (config.fastener_host_url,
                                                     config.fastener_api_token)
            response, content = http.request(url, 'GET')

            # list of instances from Google Cloud (see ./fastener/sample-output.json)
            gcinstances = json.loads(content)

            if len(gcinstances) > 0:
                message = "ok"
            else:
                message = "no instances were returned from fastener API"

        except Exception as ex:
            gcinstances = []
            slack.slack_message(
                "Tender::Exception with list query to fastener box.")
            message = "failed to contact fastener API"

        # list of instances from db
        instances = Instance.get_all()

        # bail if we didn't get any instances from Google
        if len(gcinstances) == 0:
            params = {
                "message": message,
                "gc_count": len(gcinstances),
                "db_count": len(instances)
            }

            slack.slack_message("Tender::No instances from Google?")

            self.response.headers['Content-Type'] = "application/json"
            return self.render_template('api/tender.json', **params)
            ######

        # loop through list of instances in local or production DB
        for instance in instances:
            name = instance.name
            found = False

            # loop through the instances we got from google
            for gcinstance in gcinstances:

                # check if the names match
                if name == gcinstance['name']:
                    # got a match
                    found = True

                    try:
                        # grab the IP address and status
                        instance.ip = gcinstance['networkInterfaces'][0][
                            'accessConfigs'][0]['natIP']
                    except:
                        # got limited or no data about instance
                        instance.ip = "None"

                    # leave commented out
                    slack.slack_message("Tender::%s has status %s" %
                                        (instance.name, instance.status))

                    # if not BUILDING then update status right from google
                    if instance.status not in ("BUILDING"):
                        instance.status = gcinstance['status']

                    # at this point if the instance.status is NOT BUILDING, then we grab
                    # whatever google says the instance is doing. if it's RUNNING, then
                    # we do an instance link test to ensure the fusion service is running
                    # if Fusion does not respond, we set it to CONFIGURING

                    # BUILDING state is kept until an update of BUILDING state is done
                    # see APIs

                    # are we running?
                    if instance.status == "RUNNING":
                        # check if the box is running fusion admin yet (CONFIGURING if NOT)
                        try:
                            # fast fail connection for checking if fusion is up
                            http_test = httplib2.Http(timeout=2)
                            test_url = 'http://%s:8764' % instance.ip
                            response, content = http_test.request(
                                test_url, 'GET')
                            test_status = response['status']
                        except:
                            slack.slack_message(
                                "Tender::%s FAILED Fusion port test." %
                                (instance.name))
                            test_status = "404"

                        # set admin_link if box is running and test comes back 200
                        if test_status == "200":
                            instance.admin_link = test_url

                            # build app link and update, if it exists
                            try:
                                if instance.stream.get().app_stub:
                                    app_stub = instance.stream.get().app_stub
                                    instance.app_link = "http://%s%s" % (
                                        instance.ip, app_stub)
                                else:
                                    instance.app_link = None
                            except:
                                instance.app_link = None

                        else:
                            # show the box is in CONFIGURING
                            instance.status = "CONFIGURING"
                            instance.admin_link = None
                            instance.app_link = None
                            instance.put()

                    else:  # NOT RUNNING STATUS (FROM GOOGLE) OR BUILDING (FROM DEPLOY SCRIPT)
                        # should we start it?
                        if instance.tender_action == "START":
                            # try to start it
                            http = httplib2.Http(timeout=10)
                            url = '%s/api/instance/%s/start?token=%s' % (
                                config.fastener_host_url, name,
                                config.fastener_api_token)
                            try:
                                # pull the response back TODO add error handling
                                response, content = http.request(url,
                                                                 'GET',
                                                                 None,
                                                                 headers={})

                                # update if google returns pending
                                if json.loads(content)[
                                        'status'] == "PENDING" or json.loads(
                                            content)['status'] == "DONE":
                                    params = {
                                        "response": "success",
                                        "message": "instance %s started" % name
                                    }
                                    instance.status = "PROVISIONING"
                                    instance.tender_action = "NONE"
                                    instance.started = datetime.datetime.now()
                                    instance.put()

                                    slack.slack_message(
                                        "Tender::%s wanted and got a START" %
                                        (instance.name))
                                else:
                                    slack.slack_message(
                                        "Tender::%s wanted START but google reported %s"
                                        % (instance.name,
                                           json.loads(content)['status']))

                            except Exception as ex:
                                slack.slack_message(
                                    "Tender::Exception %s with %s" %
                                    (ex, instance.name))

                        else:
                            # slack.slack_message("Tender::%s not requesting any actions." % instance.name)
                            pass

                    # instance has been terminated
                    if instance.status == "TERMINATED":
                        # set start time to far in the past
                        instance.started = instance.created - datetime.timedelta(
                            0, 604800)

                    # make sure we write all changes out
                    instance.put()
                    break  # no need to keep looking

                else:
                    # instance doesn't match the one we're working on
                    pass

            if not found:
                # box wasn't found on GCP (via fastener LIST call)
                slack.slack_message(
                    "Instance %s noted not being on GCP - looking" % name)

                http = httplib2.Http(timeout=10)
                url = '%s/api/instance/%s/status?token=%s' % (
                    config.fastener_host_url, name, config.fastener_api_token)

                # pull the response back
                response, content = http.request(url, 'GET', None, headers={})

                result = json.loads(content)
                if not result:
                    # we could not verify box was or wasn't running (fastener might not be running)
                    slack.slack_message("Can't tell what is going on.")
                    pass
                else:
                    try:
                        if result['error'] == "NOTFOUND":
                            slack.slack_message(
                                "Deleting instance %s from DB for not being on GCP."
                                % name)
                            instance.key.delete()
                    except:
                        # no error
                        # why are we here, we got a response this box is running
                        pass

        else:
            # no instances in db
            pass

        # cleanup stray and EOL instances
        for gcinstance in gcinstances:
            instance = Instance.get_by_name(gcinstance['name'])

            if instance:
                name = instance.name

                # if instance is expired, end it
                if instance.expires < datetime.datetime.now():
                    # make the instance call to the control box
                    try:
                        http = httplib2.Http(timeout=10)
                        url = '%s/api/instance/%s/delete?token=%s' % (
                            config.fastener_host_url, name,
                            config.fastener_api_token)

                        # pull the response back
                        response, content = http.request(url,
                                                         'GET',
                                                         None,
                                                         headers={})
                        if content['status'] == "PENDING":
                            instance.key.delete()
                            slack.slack_message(
                                "DELETING instance %s's from GCP because expired."
                                % name)

                    except:
                        slack.slack_message(
                            "ERROR: failed deleting instance %s's from GCP because expired."
                            % name)

            else:
                # instance wasn't found in db
                name = gcinstance['name']

                # make sure we don't delete non-demo or prod instances
                if 'button' in name and config.isdev == False:  # i.e. put 'button' in an instance name & this will delete the instance
                    slack.slack_message(
                        "Not found in DB. Will try to delete instance %s's from Google Cloud."
                        % name)

                    # make the instance call to the control box
                    # THIS IS THE DANGEROUS BITS
                    try:
                        http = httplib2.Http(timeout=10)
                        url = '%s/api/instance/%s/delete?token=%s' % (
                            config.fastener_host_url, name,
                            config.fastener_api_token)

                        # pull the response back
                        response, content = http.request(url,
                                                         'GET',
                                                         None,
                                                         headers={})

                        if content['status'] == "PENDING":
                            slack.slack_message(
                                "DELETING instance %s's from Google Cloud." %
                                name)
                        else:
                            slack.slack_message(
                                "ERROR: funky content returned while deleting instance %s's from Google Cloud."
                                % name)

                    except:
                        slack.slack_message(
                            "ERROR: failed deleting instance %s from Google Cloud."
                            % name)

        else:
            # no instances from cloud - this should never run
            pass

        params = {
            "message": message,
            "gc_count": len(gcinstances),
            "db_count": len(instances)
        }

        self.response.headers['Content-Type'] = "application/json"
        return self.render_template('api/tender.json', **params)
Esempio n. 8
0
    def post(self, sid=None):
        # topic (stackexchange subdomain, for now)
        topic = self.request.get('topic')
        if not topic:
            topic = "ai"

        # region
        region = self.request.get('region')
        if not region:
            region = "any"

        # preemptible request (add check user perms)
        preemptible = self.request.get('preemptible')

        if not preemptible:
            preemptible = 1  # preemptible
        else:
            if preemptible == "False" or preemptible == "false" or int(
                    preemptible) == 0:
                preemptible = 0  # not preemptible
            else:
                preemptible = 1

        # check token
        token = self.request.get('token')
        if token != "":
            user_info = User.get_by_token(token)

            if user_info:
                # look up streams
                stream = Stream.get_by_sid(sid)
                if not stream:
                    params = {
                        "response":
                        "fail",
                        "message":
                        "stream %s does not exist on these endpoints" % sid
                    }
                    return self.render_template('api/response.json', **params)

                try:
                    size = stream.instance_size
                except:
                    size = 0

                # look up user's instances
                db_instances = Instance.get_all()

                # check the user's limits
                instance_count = 0
                for db_instance in db_instances:
                    # limit to instances the user has started (doing it this way because can't figure out ndb indexing)
                    if db_instance.user == user_info.key:
                        instance_count = instance_count + 1

                # check preemtibility ability
                if preemptible == 0:
                    if user_info.superuser == False:
                        # if the user can't start preemptible tell them
                        params = {
                            "response":
                            "fail",
                            "message":
                            "token may not create non-preemptible instance"
                        }
                        return self.render_template('api/response.json',
                                                    **params)

                # warn and redirect if limit is reached
                if (instance_count + 1) > user_info.max_instances:
                    params = {
                        "response": "fail",
                        "message": "max instances reached for provided token"
                    }
                    return self.render_template('api/response.json', **params)

                # make the instance call to the control box
                http = httplib2.Http(timeout=10)

                # where and who created it
                if config.isdev:
                    iuser = "******" % ("dev", user_info.username)
                else:
                    iuser = "******" % ("prod", user_info.username)

                url = '%s/api/stream/%s?token=%s&user=%s&topic=%s&region=%s&preemptible=%s&size=%s' % (
                    config.fastener_host_url, sid, config.fastener_api_token,
                    iuser, topic, region, preemptible, size)

                try:
                    # pull the response back TODO add error handling
                    # CREATE HAPPENS HERE
                    response, content = http.request(url,
                                                     'POST',
                                                     None,
                                                     headers={})
                    gcinstance = json.loads(content)
                    name = gcinstance['instance']
                    password = gcinstance['password']

                    if name == "failed":
                        raise Exception(
                            "Instance start was delayed due to quota. Try again in a few minutes."
                        )

                    # set up an instance
                    instance = Instance(name=name,
                                        status="PROVISIONING",
                                        user=user_info.key,
                                        stream=stream.key,
                                        size=size,
                                        region=region,
                                        topic=topic,
                                        preemptible=bool(preemptible),
                                        password=password,
                                        expires=datetime.datetime.now() +
                                        datetime.timedelta(0, 604800),
                                        started=datetime.datetime.now())
                    instance.put()

                    try:
                        slack.slack_message(
                            "Instance type %s created for %s!" %
                            (stream.name, user_info.username))
                    except:
                        print "slack failing"

                    # give the db a second to update
                    if config.isdev:
                        time.sleep(3)

                    params = {'instance': instance}

                    self.response.headers['Content-Type'] = "application/json"
                    return self.render_template('api/instance.json', **params)

                except Exception as ex:
                    # the horror
                    params = {
                        "response": "fail",
                        "message": "exception thrown: %s" % ex
                    }
                    return self.render_template('api/response.json', **params)

        # no token, no user, no data
        self.response.status = '402 Payment Required'
        self.response.status_int = 402
        self.response.headers['Content-Type'] = "application/json"
        params = {
            "response": "fail",
            "message": "must include [token] with a valid token"
        }
        return self.render_template('api/response.json', **params)