Ejemplo n.º 1
0
    def test_get_infra(self):
        infra = Infrastructures("sqlite:///tmp/creds.db")
        res = infra._get_inf_db()
        str_data = '{"name": "infra_name"}'
        res.execute(
            "replace into infrastructures (infid, data) values (%s, %s)",
            ("infid", str_data))
        res.close()

        res = infra.get_infra("infid")
        self.assertEquals(res, {'name': 'infra_name'})
Ejemplo n.º 2
0
 def test_write_infra(self):
     infra = Infrastructures("sqlite:///tmp/creds.db")
     infra.write_infra("infid", {"name": "infra_name"})
     res = infra.get_infra("infid")
     self.assertEquals(res, {"name": "infra_name"})
     infra.write_infra("infid", {"state": "infra_state"})
     res = infra.get_infra("infid")
     self.assertEquals(res, {"name": "infra_name", "state": "infra_state"})
Ejemplo n.º 3
0
 def test_delete_infra(self):
     infra = Infrastructures("sqlite:///tmp/creds.db")
     infra.delete_infra("infid")
     res = infra.get_infra("infid")
     self.assertEquals(res, {})
Ejemplo n.º 4
0
def create_app(oidc_blueprint=None):
    app = Flask(__name__)
    app.wsgi_app = ProxyFix(app.wsgi_app,
                            x_for=1,
                            x_proto=1,
                            x_host=1,
                            x_port=1,
                            x_prefix=1)
    app.secret_key = "8210f566-4981-11ea-92d1-f079596e599b"
    app.config.from_json('config.json')
    settings = Settings(app.config)
    cred = Credentials(settings.db_url)
    infra = Infrastructures(settings.db_url)

    toscaTemplates = utils.loadToscaTemplates(settings.toscaDir)
    toscaInfo = utils.extractToscaInfo(settings.toscaDir,
                                       settings.toscaParamsDir, toscaTemplates)

    app.jinja_env.filters['tojson_pretty'] = utils.to_pretty_json
    app.logger.debug("TOSCA INFO: " + json.dumps(toscaInfo))

    loglevel = app.config.get("LOG_LEVEL") if app.config.get(
        "LOG_LEVEL") else "INFO"

    numeric_level = getattr(logging, loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % loglevel)

    logging.basicConfig(level=numeric_level)

    oidc_base_url = app.config['OIDC_BASE_URL']
    oidc_token_url = oidc_base_url + '/token'
    oidc_refresh_url = oidc_base_url + '/token'
    oidc_authorization_url = oidc_base_url + '/authorize'

    if not oidc_blueprint:
        oidc_blueprint = OAuth2ConsumerBlueprint(
            "oidc",
            __name__,
            client_id=app.config['OIDC_CLIENT_ID'],
            client_secret=app.config['OIDC_CLIENT_SECRET'],
            scope=app.config['OIDC_SCOPES'],
            base_url=oidc_base_url,
            token_url=oidc_token_url,
            auto_refresh_url=oidc_refresh_url,
            authorization_url=oidc_authorization_url,
            redirect_to='home')
    app.register_blueprint(oidc_blueprint, url_prefix="/login")

    @app.before_request
    def before_request_checks():
        if 'external_links' not in session:
            session['external_links'] = settings.external_links
        g.analytics_tag = settings.analytics_tag
        g.settings = settings

    def authorized_with_valid_token(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):

            try:
                if not oidc_blueprint.session.authorized or 'username' not in session:
                    return redirect(url_for('login'))

                if oidc_blueprint.session.token['expires_in'] < 20:
                    app.logger.debug("Force refresh token")
                    oidc_blueprint.session.get('/userinfo')
            except (InvalidTokenError, TokenExpiredError):
                flash("Token expired.", 'warning')
                return redirect(url_for('login'))

            return f(*args, **kwargs)

        return decorated_function

    @app.route('/settings')
    @authorized_with_valid_token
    def show_settings():
        return render_template('settings.html',
                               oidc_url=settings.oidcUrl,
                               im_url=settings.imUrl)

    @app.route('/login')
    def login():
        session.clear()
        return render_template('home.html', oidc_name=settings.oidcName)

    @app.route('/')
    def home():
        if not oidc_blueprint.session.authorized:
            return redirect(url_for('login'))

        try:
            account_info = oidc_blueprint.session.get(
                urlparse(settings.oidcUrl)[2] + "/userinfo")
        except (InvalidTokenError, TokenExpiredError):
            flash("Token expired.", 'warning')
            return redirect(url_for('login'))

        if account_info.ok:
            account_info_json = account_info.json()

            session["vos"] = None
            if 'eduperson_entitlement' in account_info_json:
                session["vos"] = utils.getUserVOs(
                    account_info_json['eduperson_entitlement'])

            if settings.oidcGroups:
                user_groups = []
                if 'groups' in account_info_json:
                    user_groups = account_info_json['groups']
                elif 'eduperson_entitlement' in account_info_json:
                    user_groups = account_info_json['eduperson_entitlement']
                if not set(settings.oidcGroups).issubset(user_groups):
                    app.logger.debug(
                        "No match on group membership. User group membership: "
                        + json.dumps(user_groups))
                    message = Markup(
                        'You need to be a member of the following groups: {0}. <br>'
                        ' Please, visit <a href="{1}">{1}</a> and apply for the requested '
                        'membership.'.format(json.dumps(settings.oidcGroups),
                                             settings.oidcUrl))
                    raise Forbidden(description=message)

            session['userid'] = account_info_json['sub']
            if 'name' in account_info_json:
                session['username'] = account_info_json['name']
            else:
                session['username'] = ""
                if 'given_name' in account_info_json:
                    session['username'] = account_info_json['given_name']
                if 'family_name' in account_info_json:
                    session[
                        'username'] += " " + account_info_json['family_name']
                if session['username'] == "":
                    session['username'] = account_info_json['sub']
            if 'email' in account_info_json:
                session['gravatar'] = utils.avatar(account_info_json['email'],
                                                   26)
            else:
                session['gravatar'] = utils.avatar(account_info_json['sub'],
                                                   26)

            return render_template('portfolio.html', templates=toscaInfo)
        else:
            flash("Error getting User info: \n" + account_info.text, 'error')
            return render_template('home.html', oidc_name=settings.oidcName)

    @app.route('/vminfo/<infid>/<vmid>')
    @authorized_with_valid_token
    def showvminfo(infid=None, vmid=None):
        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data, "Accept": "application/json"}

        url = "%s/infrastructures/%s/vms/%s" % (settings.imUrl, infid, vmid)
        response = requests.get(url, headers=headers)

        vminfo = {}
        state = ""
        nets = ""
        deployment = ""
        if not response.ok:
            flash("Error retrieving VM info: \n" + response.text, 'error')
        else:
            app.logger.debug("VM Info: %s" % response.text)
            vminfo = utils.format_json_radl(response.json()["radl"])
            if "cpu.arch" in vminfo:
                del vminfo["cpu.arch"]
            if "state" in vminfo:
                state = vminfo["state"]
                del vminfo["state"]
            if "provider.type" in vminfo:
                deployment = vminfo["provider.type"]
                del vminfo["provider.type"]
            if "provider.host" in vminfo:
                if "provider.port" in vminfo:
                    deployment += ": %s:%s" % (vminfo["provider.host"],
                                               vminfo["provider.port"])
                    del vminfo["provider.port"]
                else:
                    deployment += ": " + vminfo["provider.host"]
                del vminfo["provider.host"]

            cont = 0
            while "net_interface.%s.ip" % cont in vminfo:
                if cont > 0:
                    nets += Markup('<br/>')
                nets += Markup('<i class="fa fa-network-wired"></i>')
                nets += " %s: %s" % (cont,
                                     vminfo["net_interface.%s.ip" % cont])
                del vminfo["net_interface.%s.ip" % cont]
                cont += 1

            cont = 0
            while "net_interface.%s.connection" % cont in vminfo:
                del vminfo["net_interface.%s.connection" % cont]
                cont += 1

            for elem in vminfo:
                if elem.endswith("size") and isinstance(vminfo[elem], int):
                    vminfo[elem] = "%d GB" % (vminfo[elem] / 1073741824)

        return render_template('vminfo.html',
                               infid=infid,
                               vmid=vmid,
                               vminfo=vminfo,
                               state=state,
                               nets=nets,
                               deployment=deployment)

    @app.route('/managevm/<op>/<infid>/<vmid>')
    @authorized_with_valid_token
    def managevm(op=None, infid=None, vmid=None):
        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data, "Accept": "application/json"}

        op = op.lower()
        if op in ["stop", "start", "reboot"]:
            url = "%s/infrastructures/%s/vms/%s/%s" % (settings.imUrl, infid,
                                                       vmid, op)
            response = requests.put(url, headers=headers)
        elif op == "terminate":
            url = "%s/infrastructures/%s/vms/%s" % (settings.imUrl, infid,
                                                    vmid)
            response = requests.delete(url, headers=headers)
        else:
            flash("Error: invalid operation: %s." % op, 'error')
            return redirect(url_for('showinfrastructures'))

        if response.ok:
            flash("Operation '%s' successfully made on VM ID: %s" % (op, vmid),
                  'info')
        else:
            flash(
                "Error making %s op on VM %s: \n%s" %
                (op, vmid, response.text), 'error')

        if op == "terminate":
            return redirect(url_for('showinfrastructures'))
        else:
            return redirect(url_for('showvminfo', infid=infid, vmid=vmid))

    @app.route('/infrastructures')
    @authorized_with_valid_token
    def showinfrastructures():
        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data, "Accept": "application/json"}

        url = "%s/infrastructures" % settings.imUrl
        response = requests.get(url, headers=headers)

        infrastructures = {}
        if not response.ok:
            flash("Error retrieving infrastructure list: \n" + response.text,
                  'error')
        else:
            app.logger.debug("Infrastructures: %s" % response.text)
            state_res = response.json()
            if "uri-list" in state_res:
                inf_id_list = [elem["uri"] for elem in state_res["uri-list"]]
            else:
                inf_id_list = []
            for inf_id in inf_id_list:
                url = "%s/state" % inf_id
                response = requests.get(url, headers=headers)
                if not response.ok:
                    flash(
                        "Error retrieving infrastructure %s state: \n%s" %
                        (inf_id, response.text), 'warning')
                else:
                    inf_state = response.json()
                    infrastructures[os.path.basename(
                        inf_id)] = inf_state['state']

                    try:
                        infra_name = infra.get_infra(
                            os.path.basename(inf_id))["name"]
                    except Exception:
                        infra_name = ""

                    infrastructures[os.path.basename(
                        inf_id)]['name'] = infra_name

        return render_template('infrastructures.html',
                               infrastructures=infrastructures)

    @app.route('/reconfigure/<infid>')
    @authorized_with_valid_token
    def infreconfigure(infid=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s/reconfigure" % (settings.imUrl, infid)
        response = requests.put(url, headers=headers)

        if response.ok:
            flash("Infrastructure successfuly reconfigured.", "info")
        else:
            flash("Error reconfiguring Infrastructure: \n" + response.text,
                  "error")

        return redirect(url_for('showinfrastructures'))

    @app.route('/template/<infid>')
    @authorized_with_valid_token
    def template(infid=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s/tosca" % (settings.imUrl, infid)
        response = requests.get(url, headers=headers)

        if not response.ok:
            flash("Error getting template: \n" + response.text, "error")
            template = ""
        else:
            template = response.text
        return render_template('deptemplate.html', template=template)

    @app.route('/log/<infid>')
    @authorized_with_valid_token
    def inflog(infid=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s/contmsg" % (settings.imUrl, infid)
        response = requests.get(url, headers=headers, verify=False)

        if not response.ok:
            log = "Not found"
        else:
            log = response.text
        return render_template('inflog.html', log=log)

    @app.route('/vmlog/<infid>/<vmid>')
    @authorized_with_valid_token
    def vmlog(infid=None, vmid=None):

        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s/vms/%s/contmsg" % (settings.imUrl, infid,
                                                        vmid)
        response = requests.get(url, headers=headers, verify=False)

        if not response.ok:
            log = "Not found"
        else:
            log = response.text
        return render_template('inflog.html', log=log, vmid=vmid)

    @app.route('/outputs/<infid>')
    @authorized_with_valid_token
    def infoutputs(infid=None):

        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s/outputs" % (settings.imUrl, infid)
        response = requests.get(url, headers=headers, verify=False)

        if not response.ok:
            outputs = {}
        else:
            outputs = response.json()["outputs"]
            for elem in outputs:
                if isinstance(outputs[elem],
                              str) and (outputs[elem].startswith('http://') or
                                        outputs[elem].startswith('https://')):
                    outputs[elem] = Markup(
                        "<a href='%s' target='_blank'>%s</a>" %
                        (outputs[elem], outputs[elem]))

        return render_template('outputs.html', infid=infid, outputs=outputs)

    @app.route('/delete/<infid>/<force>')
    @authorized_with_valid_token
    def infdel(infid=None, force=0):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data}

        url = "%s/infrastructures/%s?async=1" % (settings.imUrl, infid)
        if force:
            url += "&force=1"
        response = requests.delete(url, headers=headers)

        if not response.ok:
            flash("Error deleting infrastructure: " + response.text, "error")
        else:
            flash("Infrastructure '%s' successfuly deleted." % infid, "info")
            try:
                infra.delete_infra(infid)
            except Exception as ex:
                flash("Error deleting infrastructure name: %s" + str(ex),
                      "warning")

        return redirect(url_for('showinfrastructures'))

    @app.route('/configure')
    @authorized_with_valid_token
    def configure():

        selected_tosca = request.args['selected_tosca']

        app.logger.debug("Template: " + json.dumps(toscaInfo[selected_tosca]))

        vos = utils.getStaticVOs()
        vos.extend(appdb.get_vo_list())
        vos = list(set(vos))
        if "vos" in session and session["vos"]:
            vos = [vo for vo in vos if vo in session["vos"]]

        return render_template('createdep.html',
                               template=toscaInfo[selected_tosca],
                               selectedTemplate=selected_tosca,
                               vos=vos)

    @app.route('/sites/<vo>')
    def getsites(vo=None):
        res = ""
        appdb_sites = appdb.get_sites(vo)
        for site_name, site in appdb_sites.items():
            if site["state"]:
                site["state"] = " (WARNING: %s state!)" % site["state"]
            res += '<option name="selectedSite" value=%s>%s%s</option>' % (
                site_name, site_name, site["state"])

        for site_name, _ in utils.getStaticSites(vo).items():
            # avoid site duplication
            if site_name not in appdb_sites:
                res += '<option name="selectedSite" value=%s>%s</option>' % (
                    site_name, site_name)

        return res

    @app.route('/images/<site>/<vo>')
    @authorized_with_valid_token
    def getimages(site=None, vo=None):
        res = ""
        local = request.args.get('local', None)
        if local:
            access_token = oidc_blueprint.session.token['access_token']
            for image_name, image_id in utils.get_site_images(
                    site, vo, access_token, cred, session["userid"]):
                res += '<option name="selectedSiteImage" value=%s>%s</option>' % (
                    image_id, image_name)
        else:
            site_id = utils.getCachedSiteList()[site]['id']
            for image in appdb.get_images(site_id, vo):
                res += '<option name="selectedImage" value=%s>%s</option>' % (
                    image, image)
        return res

    @app.route('/usage/<site>/<vo>')
    @authorized_with_valid_token
    def getusage(site=None, vo=None):
        try:
            access_token = oidc_blueprint.session.token['access_token']
            quotas_dict = utils.get_site_usage(site, vo, access_token, cred,
                                               session["userid"])
            return json.dumps(quotas_dict)
        except Exception as ex:
            return "Error loading site quotas: %s!" % str(ex), 400

    def add_image_to_template(template, image):
        # Add the image to all compute nodes

        for node in list(
                template['topology_template']['node_templates'].values()):
            if node["type"] == "tosca.nodes.indigo.Compute":
                node["capabilities"]["os"]["properties"]["image"] = image

        app.logger.debug(yaml.dump(template, default_flow_style=False))

        return template

    def add_auth_to_template(template, auth_data):
        # Add the auth_data ElasticCluster node

        for node in list(
                template['topology_template']['node_templates'].values()):
            if node["type"] == "tosca.nodes.ec3.ElasticCluster":
                if "properties" not in node:
                    node["properties"] = {}
                node["properties"]["im_auth"] = auth_data

        app.logger.debug(yaml.dump(template, default_flow_style=False))

        return template

    def set_inputs_to_template(template, inputs):
        # Add the image to all compute nodes

        for name, value in template['topology_template']['inputs'].items():
            if name in inputs:
                if value["type"] == "integer":
                    value["default"] = int(inputs[name])
                elif value["type"] == "float":
                    value["default"] = float(inputs[name])
                elif value["type"] == "boolean":
                    if inputs[name].lower() in ['yes', 'true', '1']:
                        value["default"] = True
                    else:
                        value["default"] = False
                # Special case for ports, convert a comma separated list of ints
                # to a PortSpec map
                elif value["type"] == "map" and name == "ports":
                    ports = inputs[name].split(",")
                    ports_value = {}
                    for port in ports:
                        # Should we also open UDP?
                        ports_value["port_%s" % port] = {
                            "protocol": "tcp",
                            "source": int(port)
                        }
                    value["default"] = ports_value
                else:
                    value["default"] = inputs[name]

        app.logger.debug(yaml.dump(template, default_flow_style=False))
        return template

    @app.route('/submit', methods=['POST'])
    @authorized_with_valid_token
    def createdep():

        form_data = request.form.to_dict()
        vo = form_data['extra_opts.selectedVO']
        site = form_data['extra_opts.selectedSite']

        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"], vo, site)

        app.logger.debug("Form data: " + json.dumps(request.form.to_dict()))

        with io.open(settings.toscaDir +
                     request.args.get('template')) as stream:
            template = yaml.full_load(stream)

            if form_data['extra_opts.selectedImage'] != "":
                image = "appdb://%s/%s?%s" % (
                    form_data['extra_opts.selectedSite'],
                    form_data['extra_opts.selectedImage'],
                    form_data['extra_opts.selectedVO'])
            elif form_data['extra_opts.selectedSiteImage'] != "":
                site_url = utils.get_ost_image_url(
                    form_data['extra_opts.selectedSite'])
                image = "ost://%s/%s" % (
                    site_url, form_data['extra_opts.selectedSiteImage'])
            else:
                flash("No correct image selected.", "error")
                return redirect(url_for('showinfrastructures'))

            template = add_image_to_template(template, image)

            template = add_auth_to_template(template, auth_data)

            inputs = {
                k: v
                for (k, v) in form_data.items()
                if not k.startswith("extra_opts.")
            }

            app.logger.debug("Parameters: " + json.dumps(inputs))

            template = set_inputs_to_template(template, inputs)

            payload = yaml.dump(template,
                                default_flow_style=False,
                                sort_keys=False)

        headers = {"Authorization": auth_data, "Content-Type": "text/yaml"}

        url = "%s/infrastructures?async=1" % settings.imUrl
        response = requests.post(url, headers=headers, data=payload)

        if not response.ok:
            flash("Error creating infrastrucrure: \n" + response.text, "error")
        else:
            try:
                inf_id = os.path.basename(response.text)
                infra.write_infra(inf_id, {"name": form_data['infra_name']})
            except Exception as ex:
                flash("Error storing Infrastructure name: %s" % str(ex),
                      "warning")

        return redirect(url_for('showinfrastructures'))

    @app.route('/manage_creds')
    @authorized_with_valid_token
    def manage_creds():
        sites = {}

        try:
            sites = utils.getCachedSiteList()
        except Exception as e:
            flash("Error retrieving sites list: \n" + str(e), 'warning')

        return render_template('service_creds.html', sites=sites)

    @app.route('/write_creds', methods=['GET', 'POST'])
    @authorized_with_valid_token
    def write_creds():
        serviceid = request.args.get('service_id', "")
        servicename = request.args.get('service_name', "")
        app.logger.debug("service_id={}".format(serviceid))

        if request.method == 'GET':
            res = {}
            projects = {}
            try:
                res = cred.get_cred(servicename, session["userid"])
                projects = utils.getCachedProjectIDs(serviceid)
                app.logger.debug("projects={}".format(projects))

                if session["vos"]:
                    filter_projects = {}
                    for vo, project in projects.items():
                        if vo in session["vos"]:
                            filter_projects[vo] = project
                    projects = filter_projects
            except Exception as ex:
                flash("Error reading credentials %s!" % ex, 'error')

            return render_template('modal_creds.html',
                                   service_creds=res,
                                   service_id=serviceid,
                                   service_name=servicename,
                                   projects=projects)
        else:
            app.logger.debug("Form data: " +
                             json.dumps(request.form.to_dict()))

            creds = request.form.to_dict()
            try:
                cred.write_creds(servicename, session["userid"], creds)
                flash("Credentials successfully written!", 'info')
            except Exception as ex:
                flash("Error writing credentials %s!" % ex, 'error')

            return redirect(url_for('manage_creds'))

    @app.route('/delete_creds')
    @authorized_with_valid_token
    def delete_creds():

        serviceid = request.args.get('service_id', "")
        try:
            cred.delete_cred(serviceid, session["userid"])
            flash("Credentials successfully deleted!", 'info')
        except Exception as ex:
            flash("Error deleting credentials %s!" % ex, 'error')

        return redirect(url_for('manage_creds'))

    @app.route('/addresourcesform/<infid>')
    @authorized_with_valid_token
    def addresourcesform(infid=None):

        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data, "Accept": "text/plain"}

        url = "%s/infrastructures/%s/radl" % (settings.imUrl, infid)
        response = requests.get(url, headers=headers)

        if response.ok:
            systems = []
            try:
                radl = radl_parse.parse_radl(response.text)
                systems = radl.systems
            except Exception as ex:
                flash("Error parsing RADL: \n%s" % str(ex), 'error')

            return render_template('addresource.html',
                                   infid=infid,
                                   systems=systems)
        else:
            flash("Error getting RADL: \n%s" % (response.text), 'error')
            return redirect(url_for('showinfrastructures'))

    @app.route('/addresources/<infid>', methods=['POST'])
    @authorized_with_valid_token
    def addresources(infid=None):

        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        headers = {"Authorization": auth_data, "Accept": "text/plain"}

        form_data = request.form.to_dict()

        url = "%s/infrastructures/%s/radl" % (settings.imUrl, infid)
        response = requests.get(url, headers=headers)

        if response.ok:
            radl = None
            try:
                radl = radl_parse.parse_radl(response.text)
                radl.deploys = []
                for system in radl.systems:
                    if "%s_num" % system.name in form_data:
                        vm_num = int(form_data["%s_num" % system.name])
                        if vm_num > 0:
                            radl.deploys.append(deploy(system.name, vm_num))
            except Exception as ex:
                flash(
                    "Error parsing RADL: \n%s\n%s" % (str(ex), response.text),
                    'error')

            if radl:
                headers = {
                    "Authorization": auth_data,
                    "Accept": "application/json"
                }
                url = "%s/infrastructures/%s" % (settings.imUrl, infid)
                response = requests.post(url, headers=headers, data=str(radl))

                if response.ok:
                    num = len(response.json()["uri-list"])
                    flash("%d nodes added successfully" % num, 'info')
                else:
                    flash("Error adding nodesL: \n%s" % (response.text),
                          'error')

            return redirect(url_for('showinfrastructures'))
        else:
            flash("Error getting RADL: \n%s" % (response.text), 'error')
            return redirect(url_for('showinfrastructures'))

    @app.route('/logout')
    def logout():
        session.clear()
        oidc_blueprint.session.get("/logout")
        return redirect(url_for('login'))

    @app.errorhandler(403)
    def forbidden(error):
        return render_template('error_pages/403.html',
                               message=error.description)

    @app.errorhandler(404)
    def page_not_found(error):
        app.logger.error('Page not found: %s', (request.path))
        return render_template('error_pages/404.html'), 404

    @app.errorhandler(500)
    def internal_server_error(error):
        app.logger.error('Server Error: %s', (error))
        return render_template(
            'error_pages/500.html',
            support_email=app.config.get('SUPPORT_EMAIL')), 500

    return app
Ejemplo n.º 5
0
def create_app(oidc_blueprint=None):
    app = Flask(__name__)
    app.wsgi_app = ProxyFix(app.wsgi_app,
                            x_for=1,
                            x_proto=1,
                            x_host=1,
                            x_port=1,
                            x_prefix=1)
    app.secret_key = "8210f566-4981-11ea-92d1-f079596e599b"
    app.config.from_json('config.json')
    settings = Settings(app.config)
    if 'CREDS_KEY' in os.environ:
        key = os.environ['CREDS_KEY']
    else:
        key = None
    CSRFProtect(app)
    cred = Credentials(settings.db_url, key)
    infra = Infrastructures(settings.db_url)
    im = InfrastructureManager(settings.imUrl)

    # To Reload internally the site cache
    scheduler = APScheduler()
    scheduler.api_enabled = False
    scheduler.init_app(app)
    scheduler.start()

    toscaTemplates = utils.loadToscaTemplates(settings.toscaDir)
    toscaInfo = utils.extractToscaInfo(settings.toscaDir,
                                       settings.toscaParamsDir, toscaTemplates)

    app.jinja_env.filters['tojson_pretty'] = utils.to_pretty_json
    app.logger.debug("TOSCA INFO: " + json.dumps(toscaInfo))

    loglevel = app.config.get("LOG_LEVEL") if app.config.get(
        "LOG_LEVEL") else "INFO"

    numeric_level = getattr(logging, loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % loglevel)

    logging.basicConfig(level=numeric_level)

    oidc_base_url = app.config['OIDC_BASE_URL']
    oidc_token_url = oidc_base_url + '/token'
    oidc_refresh_url = oidc_base_url + '/token'
    oidc_authorization_url = oidc_base_url + '/authorize'

    if not oidc_blueprint:
        oidc_blueprint = OAuth2ConsumerBlueprint(
            "oidc",
            __name__,
            client_id=app.config['OIDC_CLIENT_ID'],
            client_secret=app.config['OIDC_CLIENT_SECRET'],
            scope=app.config['OIDC_SCOPES'],
            base_url=oidc_base_url,
            token_url=oidc_token_url,
            auto_refresh_url=oidc_refresh_url,
            authorization_url=oidc_authorization_url,
            redirect_to='home')
    app.register_blueprint(oidc_blueprint, url_prefix="/login")

    @app.before_request
    def before_request_checks():
        if 'external_links' not in session:
            session['external_links'] = settings.external_links
        g.analytics_tag = settings.analytics_tag
        g.settings = settings

    def authorized_with_valid_token(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):

            if settings.debug_oidc_token:
                oidc_blueprint.session.token = {
                    'access_token': settings.debug_oidc_token
                }
            else:
                try:
                    if not oidc_blueprint.session.authorized or 'username' not in session:
                        return redirect(url_for('login'))

                    if oidc_blueprint.session.token['expires_in'] < 20:
                        app.logger.debug("Force refresh token")
                        oidc_blueprint.session.get('/userinfo')
                except (InvalidTokenError, TokenExpiredError):
                    flash("Token expired.", 'warning')
                    return redirect(url_for('login'))

            return f(*args, **kwargs)

        return decorated_function

    @app.route('/settings')
    @authorized_with_valid_token
    def show_settings():
        imUrl = "%s (v. %s)" % (settings.imUrl, im.get_version())
        return render_template('settings.html',
                               oidc_url=settings.oidcUrl,
                               im_url=imUrl)

    @app.route('/login')
    def login():
        session.clear()
        return render_template('home.html', oidc_name=settings.oidcName)

    @app.route('/')
    def home():
        if settings.debug_oidc_token:
            session["vos"] = None
            session['userid'] = "userid"
            session['username'] = "******"
            session['gravatar'] = ""
            return render_template('portfolio.html', templates=toscaInfo)
        else:
            if not oidc_blueprint.session.authorized:
                return redirect(url_for('login'))

            try:
                account_info = oidc_blueprint.session.get(
                    urlparse(settings.oidcUrl)[2] + "/userinfo")
            except (InvalidTokenError, TokenExpiredError):
                flash("Token expired.", 'warning')
                return redirect(url_for('login'))

            if account_info.ok:
                account_info_json = account_info.json()

                session["vos"] = None
                if 'eduperson_entitlement' in account_info_json:
                    session["vos"] = utils.getUserVOs(
                        account_info_json['eduperson_entitlement'])

                if settings.oidcGroups:
                    user_groups = []
                    if 'groups' in account_info_json:
                        user_groups = account_info_json['groups']
                    elif 'eduperson_entitlement' in account_info_json:
                        user_groups = account_info_json[
                            'eduperson_entitlement']
                    if not set(settings.oidcGroups).issubset(user_groups):
                        app.logger.debug(
                            "No match on group membership. User group membership: "
                            + json.dumps(user_groups))
                        message = Markup(
                            'You need to be a member of the following groups: {0}. <br>'
                            ' Please, visit <a href="{1}">{1}</a> and apply for the requested '
                            'membership.'.format(
                                json.dumps(settings.oidcGroups),
                                settings.oidcUrl))
                        raise Forbidden(description=message)

                session['userid'] = account_info_json['sub']
                if 'name' in account_info_json:
                    session['username'] = account_info_json['name']
                else:
                    session['username'] = ""
                    if 'given_name' in account_info_json:
                        session['username'] = account_info_json['given_name']
                    if 'family_name' in account_info_json:
                        session['username'] += " " + account_info_json[
                            'family_name']
                    if session['username'] == "":
                        session['username'] = account_info_json['sub']
                if 'email' in account_info_json:
                    session['gravatar'] = utils.avatar(
                        account_info_json['email'], 26)
                else:
                    session['gravatar'] = utils.avatar(
                        account_info_json['sub'], 26)

                return render_template('portfolio.html', templates=toscaInfo)
            else:
                flash("Error getting User info: \n" + account_info.text,
                      'error')
                return render_template('home.html',
                                       oidc_name=settings.oidcName)

    @app.route('/vminfo')
    @authorized_with_valid_token
    def showvminfo():
        access_token = oidc_blueprint.session.token['access_token']
        vmid = request.args['vmId']
        infid = request.args['infId']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        try:
            response = im.get_vm_info(infid, vmid, auth_data)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        vminfo = {}
        state = ""
        nets = ""
        disks = ""
        deployment = ""
        if not response.ok:
            flash("Error retrieving VM info: \n" + response.text, 'error')
        else:
            app.logger.debug("VM Info: %s" % response.text)
            radl_json = response.json()["radl"]
            outports = utils.get_out_ports(radl_json)
            vminfo = utils.format_json_radl(radl_json)
            if "cpu.arch" in vminfo:
                del vminfo["cpu.arch"]
            if "state" in vminfo:
                state = vminfo["state"]
                del vminfo["state"]
            if "provider.type" in vminfo:
                deployment = vminfo["provider.type"]
                del vminfo["provider.type"]
            if "provider.host" in vminfo:
                if "provider.port" in vminfo:
                    deployment += ": %s:%s" % (vminfo["provider.host"],
                                               vminfo["provider.port"])
                    del vminfo["provider.port"]
                else:
                    deployment += ": " + vminfo["provider.host"]
                del vminfo["provider.host"]
            if "disk.0.os.name" in vminfo:
                del vminfo["disk.0.os.name"]

            cont = 0
            while "net_interface.%s.connection" % cont in vminfo:
                if "net_interface.%s.ip" % cont in vminfo:
                    if cont > 0:
                        nets += Markup('<br/>')
                    nets += Markup('<i class="fa fa-network-wired"></i>')
                    nets += Markup(
                        ' <span class="badge badge-secondary">%s</span>' %
                        cont)
                    nets += ": %s" % vminfo["net_interface.%s.ip" % cont]
                    del vminfo["net_interface.%s.ip" % cont]
                    if "net_interface.%s.dns_name" % cont in vminfo:
                        nets += " (%s)" % vminfo["net_interface.%s.dns_name" %
                                                 cont]
                        del vminfo["net_interface.%s.dns_name" % cont]

                cont += 1

            cont = 0
            while "net_interface.%s.connection" % cont in vminfo:
                del vminfo["net_interface.%s.connection" % cont]
                cont += 1

            for elem in vminfo:
                if elem.endswith("size") and isinstance(vminfo[elem], int):
                    vminfo[elem] = "%d GB" % (vminfo[elem] / 1073741824)

            cont = 0
            while "disk.%s.size" % cont in vminfo or "disk.%s.image.url" % cont in vminfo:
                if cont > 0:
                    disks += Markup('<br/>')
                disks += Markup(
                    '<i class="fa fa-database"></i> <span class="badge badge-secondary">'
                    '%s</span><br/>' % cont)

                prop_map = {
                    "size": "Size",
                    "image.url": "URL",
                    "device": "Device",
                    "mount_path": "Mount Path",
                    "fstype": "F.S. type",
                    "os.flavour": "O.S. Flavor",
                    "os.version": "O.S. Version"
                }
                for name, label in prop_map.items():
                    prop = "disk.%s.%s" % (cont, name)
                    if prop in vminfo:
                        disks += Markup('&nbsp;&nbsp;')
                        disks += "- %s: %s" % (label, vminfo[prop])
                        disks += Markup('<br/>')
                        del vminfo[prop]

                cont += 1

            str_outports = ""
            for port in outports:
                str_outports += Markup(
                    '<i class="fas fa-project-diagram"></i> <span class="badge '
                    'badge-secondary">%s</span>' % port.get_remote_port())
                if not port.is_range():
                    if port.get_remote_port() != port.get_local_port():
                        str_outports += Markup(
                            ' <i class="fas fa-long-arrow-alt-right">'
                            '</i> <span class="badge badge-secondary">%s</span>'
                            % port.get_local_port())
                else:
                    str_outports += Markup(
                        ' : </i> <span class="badge badge-secondary">%s</span>'
                        % port.get_local_port())
                str_outports += Markup('<br/>')

        return render_template('vminfo.html',
                               infid=infid,
                               vmid=vmid,
                               vminfo=vminfo,
                               outports=str_outports,
                               state=state,
                               nets=nets,
                               deployment=deployment,
                               disks=disks)

    @app.route('/managevm/<op>/<infid>/<vmid>', methods=['POST'])
    @authorized_with_valid_token
    def managevm(op=None, infid=None, vmid=None):
        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        try:
            if op == "reconfigure":
                response = im.reconfigure_inf(infid, auth_data, [vmid])
            if op == "resize":
                form_data = request.form.to_dict()
                cpu = int(form_data['cpu'])
                memory = int(form_data['memory'])
                system_name = form_data['system_name']

                radl = "system %s (cpu.count >= %d and memory.size >= %dg)" % (
                    system_name, cpu, memory)
                response = im.resize_vm(infid, vmid, radl, auth_data)
            else:
                response = im.manage_vm(op, infid, vmid, auth_data)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')
            return redirect(url_for('showinfrastructures'))

        if response.ok:
            flash("Operation '%s' successfully made on VM ID: %s" % (op, vmid),
                  'info')
        else:
            flash(
                "Error making %s op on VM %s: \n%s" %
                (op, vmid, response.text), 'error')

        if op == "terminate":
            return redirect(url_for('showinfrastructures'))
        else:
            return redirect(url_for('showvminfo', infId=infid, vmId=vmid))

    @app.route('/infrastructures')
    @authorized_with_valid_token
    def showinfrastructures():
        access_token = oidc_blueprint.session.token['access_token']

        reload_infid = None
        if 'reload' in request.args:
            reload_infid = request.args['reload']

        auth_data = "type = InfrastructureManager; token = %s" % access_token
        inf_list = []
        try:
            inf_list = im.get_inf_list(auth_data)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        infrastructures = {}
        for inf_id in inf_list:
            infrastructures[inf_id] = {}
            try:
                infra_data = infra.get_infra(inf_id)
            except Exception:
                infra_data = {}
            infrastructures[inf_id] = {'name': '', 'state': {}}
            if 'name' in infra_data:
                infrastructures[inf_id]['name'] = infra_data["name"]
            if 'state' in infra_data:
                infrastructures[inf_id]['state'] = infra_data["state"]

        return render_template('infrastructures.html',
                               infrastructures=infrastructures,
                               reload=reload_infid)

    @app.route('/infrastructures/state')
    @authorized_with_valid_token
    def infrastructure_state():
        access_token = oidc_blueprint.session.token['access_token']
        infid = request.args['infid']
        if not infid:
            return {"state": "error", "vm_states": {}}

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        try:
            state = im.get_inf_state(infid, auth_data)
            try:
                infra.write_infra(infid, {"state": state})
            except Exception as ex:
                app.logger.error("Error saving infrastructure state: %s" % ex)
            return state
        except Exception:
            return {"state": "error", "vm_states": {}}

    @app.route('/template/<infid>')
    @authorized_with_valid_token
    def template(infid=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        template = ""
        try:
            response = im.get_inf_property(infid, 'tosca', auth_data)
            if not response.ok:
                raise Exception(response.text)
            template = response.text
        except Exception as ex:
            flash("Error getting template: \n%s" % ex, "error")

        return render_template('deptemplate.html', template=template)

    def add_colors(log):
        """Add color in error messages in logs."""
        res = ""
        lines = log.split('\n')
        for n, line in enumerate(lines):
            if "ERROR executing task" in line or (
                    "fatal: " in line and "...ignoring" not in lines[n + 1]):
                res += Markup(
                    '<span class="bg-danger text-white">%s</span><br>' % line)
            else:
                res += Markup("%s<br>" % line)
        return res

    @app.route('/log/<infid>')
    @authorized_with_valid_token
    def inflog(infid=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        log = "Not found"
        try:
            response = im.get_inf_property(infid, 'contmsg', auth_data)
            if not response.ok:
                raise Exception(response.text)
            log = add_colors(response.text)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        return render_template('inflog.html', log=log)

    @app.route('/vmlog/<infid>/<vmid>')
    @authorized_with_valid_token
    def vmlog(infid=None, vmid=None):

        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        log = "Not found"
        try:
            response = im.get_vm_contmsg(infid, vmid, auth_data)
            if not response.ok:
                raise Exception(response.text)
            log = add_colors(response.text)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        return render_template('inflog.html', log=log, vmid=vmid)

    @app.route('/outputs/<infid>')
    @authorized_with_valid_token
    def infoutputs(infid=None):

        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        outputs = {}
        try:
            response = im.get_inf_property(infid, 'outputs', auth_data)
            if not response.ok:
                raise Exception(response.text)

            outputs = response.json()["outputs"]
            for elem in outputs:
                if isinstance(outputs[elem],
                              str) and (outputs[elem].startswith('http://') or
                                        outputs[elem].startswith('https://')):
                    outputs[elem] = Markup(
                        "<a href='%s' target='_blank'>%s</a>" %
                        (outputs[elem], outputs[elem]))
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        return render_template('outputs.html', infid=infid, outputs=outputs)

    @app.route('/configure')
    @authorized_with_valid_token
    def configure():

        selected_tosca = request.args['selected_tosca']

        app.logger.debug("Template: " + json.dumps(toscaInfo[selected_tosca]))

        creds = cred.get_creds(session['userid'], 1)

        return render_template('createdep.html',
                               template=toscaInfo[selected_tosca],
                               selectedTemplate=selected_tosca,
                               creds=creds)

    @app.route('/vos')
    def getvos():
        res = ""
        vos = utils.getStaticVOs()
        vos.extend(appdb.get_vo_list())
        vos = list(set(vos))
        if "vos" in session and session["vos"]:
            vos = [vo for vo in vos if vo in session["vos"]]
        for vo in vos:
            res += '<option name="selectedVO" value=%s>%s</option>' % (vo, vo)
        return res

    @app.route('/sites/<vo>')
    def getsites(vo=None):
        res = ""
        static_sites = utils.getStaticSites(vo)
        for site_name, site in static_sites.items():
            res += '<option name="selectedSite" value=%s>%s</option>' % (
                site['url'], site_name)

        appdb_sites = appdb.get_sites(vo)
        for site_name, site in appdb_sites.items():
            # avoid site duplication
            if site_name not in static_sites:
                if site["state"]:
                    site["state"] = " (WARNING: %s state!)" % site["state"]
                res += '<option name="selectedSite" value=%s>%s%s</option>' % (
                    site['url'], site_name, site["state"])

        return res

    @app.route('/images/<cred_id>')
    @authorized_with_valid_token
    def getimages(cred_id=None):
        res = ""
        local = request.args.get('local', None)

        if local:
            access_token = oidc_blueprint.session.token['access_token']
            auth_data = utils.getUserAuthData(access_token, cred,
                                              session["userid"], cred_id)
            try:
                response = im.get_cloud_images(cred_id, auth_data)
                if not response.ok:
                    raise Exception(response.text)
                for image in response.json()["images"]:
                    res += '<option name="selectedSiteImage" value=%s>%s</option>' % (
                        image['uri'], image['name'])
            except Exception as ex:
                res += '<option name="selectedSiteImage" value=%s>%s</option>' % (
                    ex, ex)

        else:
            site, _, vo = utils.get_site_info(cred_id, cred, session["userid"])
            for image_name, image_id in appdb.get_images(site['id'], vo):
                res += '<option name="selectedImage" value=%s>%s</option>' % (
                    image_id, image_name)
        return res

    @app.route('/usage/<cred_id>')
    @authorized_with_valid_token
    def getusage(cred_id=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"], cred_id)
        try:
            response = im.get_cloud_quotas(cred_id, auth_data)
            if not response.ok:
                raise Exception(response.text)
            return json.dumps(response.json()["quotas"])
        except Exception as ex:
            return "Error loading site quotas: %s!" % str(ex), 400

    def add_image_to_template(template, image):
        # Add the image to all compute nodes

        for node in list(
                template['topology_template']['node_templates'].values()):
            if node["type"] == "tosca.nodes.indigo.Compute":
                node["capabilities"]["os"]["properties"]["image"] = image

        app.logger.debug(yaml.dump(template, default_flow_style=False))

        return template

    def add_auth_to_template(template, auth_data):
        # Add the auth_data ElasticCluster node

        for node in list(
                template['topology_template']['node_templates'].values()):
            if node["type"] == "tosca.nodes.ec3.ElasticCluster":
                if "properties" not in node:
                    node["properties"] = {}
                node["properties"]["im_auth"] = auth_data

        app.logger.debug(yaml.dump(template, default_flow_style=False))

        return template

    def add_record_name_to_template(template, replace_name):
        # Add a random name in the DNS record name

        for node in list(
                template['topology_template']['node_templates'].values()):
            if node["type"] == "tosca.nodes.ec3.DNSRegistry":
                node["properties"]["record_name"] = node["properties"][
                    "record_name"].replace("*", replace_name)

        return template

    def set_inputs_to_template(template, inputs):
        # Add the image to all compute nodes

        for name, value in template['topology_template']['inputs'].items():
            if name in inputs:
                if value["type"] == "integer":
                    value["default"] = int(inputs[name])
                elif value["type"] == "float":
                    value["default"] = float(inputs[name])
                elif value["type"] == "boolean":
                    if inputs[name].lower() in ['yes', 'true', '1']:
                        value["default"] = True
                    else:
                        value["default"] = False
                # Special case for ports, convert a comma separated list of ints
                # to a PortSpec map
                elif value["type"] == "map" and name == "ports":
                    ports = inputs[name].split(",")
                    ports_value = {}
                    for port in ports:
                        # Should we also open UDP?
                        ports_value["port_%s" % port] = {
                            "protocol": "tcp",
                            "source": int(port)
                        }
                    value["default"] = ports_value
                else:
                    value["default"] = inputs[name]

        app.logger.debug(yaml.dump(template, default_flow_style=False))
        return template

    @app.route('/submit', methods=['POST'])
    @authorized_with_valid_token
    def createdep():

        form_data = request.form.to_dict()

        app.logger.debug("Form data: " + json.dumps(request.form.to_dict()))

        cred_id = form_data['extra_opts.selectedCred']
        cred_data = cred.get_cred(cred_id, session["userid"])
        access_token = oidc_blueprint.session.token['access_token']

        image = None
        if cred_data['type'] in [
                'fedcloud', 'OpenStack', 'OpenNebula', 'Linode', 'Orange',
                'GCE'
        ]:
            if form_data['extra_opts.selectedImage'] != "":
                site, _, vo = utils.get_site_info(cred_id, cred,
                                                  session["userid"])
                image = "appdb://%s/%s?%s" % (
                    site['name'], form_data['extra_opts.selectedImage'], vo)
            elif form_data['extra_opts.selectedSiteImage'] != "":
                image = form_data['extra_opts.selectedSiteImage']
        else:
            image_id = form_data['extra_opts.imageID']
            protocol_map = {
                'EC2': 'aws',
                'Kubernetes': 'docker',
                'Azure': 'azr'
            }
            image = "%s://%s" % (protocol_map.get(cred_data['type']), image_id)

        if not image:
            flash("No correct image specified.", "error")
            return redirect(url_for('showinfrastructures'))

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"], cred_id)

        with io.open(settings.toscaDir +
                     request.args.get('template')) as stream:
            template = yaml.full_load(stream)
            template = add_image_to_template(template, image)

        template = add_image_to_template(template, image)

        template = add_auth_to_template(template, auth_data)

        # Specially added for OSCAR clusters
        template = add_record_name_to_template(template,
                                               utils.generate_random_name())

        inputs = {
            k: v
            for (k, v) in form_data.items() if not k.startswith("extra_opts.")
        }

        app.logger.debug("Parameters: " + json.dumps(inputs))

        template = set_inputs_to_template(template, inputs)

        payload = yaml.dump(template,
                            default_flow_style=False,
                            sort_keys=False)

        try:
            response = im.create_inf(payload, auth_data)
            if not response.ok:
                raise Exception(response.text)

            try:
                inf_id = os.path.basename(response.text)
                infra.write_infra(
                    inf_id, {
                        "name": form_data['infra_name'],
                        "state": {
                            "state": "pending",
                            "vm_states": {}
                        }
                    })
            except Exception as ex:
                flash("Error storing Infrastructure name: %s" % str(ex),
                      "warning")

        except Exception as ex:
            flash("Error creating infrastrucrure: \n%s." % ex, 'error')

        return redirect(url_for('showinfrastructures'))

    @app.route('/manage_creds')
    @authorized_with_valid_token
    def manage_creds():
        creds = {}

        try:
            creds = cred.get_creds(session["userid"])

        except Exception as e:
            flash("Error retrieving credentials: \n" + str(e), 'warning')

        return render_template('service_creds.html', creds=creds)

    @app.route('/write_creds', methods=['GET', 'POST'])
    @authorized_with_valid_token
    def write_creds():
        cred_id = request.args.get('cred_id', "")
        cred_type = request.args.get('cred_type', "")
        app.logger.debug("service_id={}".format(cred_id))

        if request.method == 'GET':
            res = {}
            try:
                if cred_id:
                    res = cred.get_cred(cred_id, session["userid"])
                    cred_type = res['type']
            except Exception as ex:
                flash("Error reading credentials %s!" % ex, 'error')

            return render_template('modal_creds.html',
                                   creds=res,
                                   cred_id=cred_id,
                                   cred_type=cred_type)
        else:
            app.logger.debug("Form data: " +
                             json.dumps(request.form.to_dict()))

            creds = request.form.to_dict()
            if 'cred_id' in creds:
                cred_id = creds['cred_id']
                del creds['cred_id']
            if 'password' in request.files:
                if request.files['password'].filename != "":
                    creds['password'] = request.files['password'].read(
                    ).decode()
            try:
                if 'password' in creds and creds['password'] in [None, '']:
                    del creds['password']
                if 'csrf_token' in creds:
                    del creds['csrf_token']
                cred.write_creds(creds["id"], session["userid"], creds, cred_id
                                 in [None, ''])
                flash("Credentials successfully written!", 'info')
            except db.IntegrityError:
                flash("Error writing credentials: Duplicated Credential ID!",
                      'error')
            except Exception as ex:
                flash("Error writing credentials: %s!" % ex, 'error')

            return redirect(url_for('manage_creds'))

    @app.route('/delete_creds')
    @authorized_with_valid_token
    def delete_creds():

        cred_id = request.args.get('cred_id', "")
        try:
            cred.delete_cred(cred_id, session["userid"])
            flash("Credentials successfully deleted!", 'info')
        except Exception as ex:
            flash("Error deleting credentials %s!" % ex, 'error')

        return redirect(url_for('manage_creds'))

    @app.route('/enable_creds')
    @authorized_with_valid_token
    def enable_creds():
        cred_id = request.args.get('cred_id', "")
        enable = request.args.get('enable', 0)
        try:
            cred.enable_cred(cred_id, session["userid"], enable)
        except Exception as ex:
            flash("Error updating credentials %s!" % ex, 'error')

        return redirect(url_for('manage_creds'))

    @app.route('/addresourcesform/<infid>')
    @authorized_with_valid_token
    def addresourcesform(infid=None):

        access_token = oidc_blueprint.session.token['access_token']

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        try:
            response = im.get_inf_property(infid, 'radl', auth_data)
            if not response.ok:
                raise Exception(response.text)

            systems = []
            try:
                radl = radl_parse.parse_radl(response.text)
                systems = radl.systems
            except Exception as ex:
                flash("Error parsing RADL: \n%s" % str(ex), 'error')

            return render_template('addresource.html',
                                   infid=infid,
                                   systems=systems)
        except Exception as ex:
            flash("Error getting RADL: \n%s" % ex, 'error')
            return redirect(url_for('showinfrastructures'))

    @app.route('/addresources/<infid>', methods=['POST'])
    @authorized_with_valid_token
    def addresources(infid=None):

        access_token = oidc_blueprint.session.token['access_token']
        form_data = request.form.to_dict()

        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        try:
            response = im.get_inf_property(infid, 'radl', auth_data)
        except Exception as ex:
            flash("Error: %s." % ex, 'error')

        if response.ok:
            radl = None
            try:
                radl = radl_parse.parse_radl(response.text)
                radl.deploys = []
                for system in radl.systems:
                    sys_dep = deploy(system.name, 0)
                    if "%s_num" % system.name in form_data:
                        vm_num = int(form_data["%s_num" % system.name])
                        if vm_num > 0:
                            sys_dep.vm_number = vm_num
                    radl.deploys.append(sys_dep)
            except Exception as ex:
                flash(
                    "Error parsing RADL: \n%s\n%s" % (str(ex), response.text),
                    'error')

            if radl:
                try:
                    response = im.addresource_inf(infid, str(radl), auth_data)
                    if not response.ok:
                        raise Exception(response.text)
                    num = len(response.json()["uri-list"])
                    flash("%d nodes added successfully" % num, 'info')
                except Exception as ex:
                    flash("Error adding nodes: \n%s\n%s" % (ex, response.text),
                          'error')

            return redirect(url_for('showinfrastructures'))
        else:
            flash("Error getting RADL: \n%s" % (response.text), 'error')
            return redirect(url_for('showinfrastructures'))

    @app.route('/manage_inf/<infid>/<op>', methods=['POST'])
    @authorized_with_valid_token
    def manage_inf(infid=None, op=None):
        access_token = oidc_blueprint.session.token['access_token']
        auth_data = utils.getUserAuthData(access_token, cred,
                                          session["userid"])
        reload = None

        try:
            if op in ["start", "stop"]:
                response = im.manage_inf(op, infid, auth_data)
                if not response.ok:
                    raise Exception(response.text)
                flash(
                    "Operation '%s' successfully made on Infrastructure ID: %s"
                    % (op, infid), 'info')
                reload = infid
            elif op == "delete":
                form_data = request.form.to_dict()
                force = False
                if 'force' in form_data and form_data['force'] != "0":
                    force = True
                # Specially added for OSCAR clusters
                success, msg = utils.delete_dns_record(infid, im, auth_data)
                if not success:
                    app.logger.error('Error deleting DNS record: %s', (msg))
                response = im.delete_inf(infid, force, auth_data)
                if not response.ok:
                    raise Exception(response.text)
                flash("Infrastructure '%s' successfuly deleted." % infid,
                      "info")
                try:
                    infra_data = infra.get_infra(infid)
                    infra_data["state"]["state"] = "deleting"
                    infra.write_infra(infid, infra_data)
                    scheduler.add_job('delete_infra_%s' % infid,
                                      delete_infra,
                                      trigger='interval',
                                      seconds=60,
                                      args=(infid, ))
                except Exception as dex:
                    app.logger.error(
                        'Error setting infra state to deleting.: %s', (dex))
            elif op == "reconfigure":
                response = im.reconfigure_inf(infid, auth_data)
                if not response.ok:
                    raise Exception(response.text)
                flash("Reconfiguration process successfuly started.", "info")
        except Exception as ex:
            flash("Error in '%s' operation: %s." % (op, ex), 'error')

        return redirect(url_for('showinfrastructures', reload=reload))

    @app.route('/logout')
    def logout():
        session.clear()
        oidc_blueprint.session.get("/logout")
        return redirect(url_for('login'))

    @app.errorhandler(403)
    def forbidden(error):
        return render_template('error_pages/403.html',
                               message=error.description)

    @app.errorhandler(404)
    def page_not_found(error):
        app.logger.error('Page not found: %s', (request.path))
        return render_template('error_pages/404.html'), 404

    @app.errorhandler(500)
    def internal_server_error(error):
        app.logger.error('Server Error: %s', (error))
        return render_template(
            'error_pages/500.html',
            support_email=app.config.get('SUPPORT_EMAIL')), 500

    # Reload internally the site cache
    @scheduler.task('interval', id='reload_sites', seconds=5)
    def reload_sites():
        scheduler.modify_job('reload_sites',
                             trigger='interval',
                             seconds=settings.appdb_cache_timeout - 30)
        with app.app_context():
            app.logger.debug('Reload Site List.')
            g.settings = settings
            utils.getCachedSiteList(True)

    def delete_infra(infid):
        infra.delete_infra(infid)
        scheduler.delete_job('delete_infra_%s' % infid)

    return app