diff --git a/labapp/app/app.py b/labapp/app/app.py index d62c698..234736c 100644 --- a/labapp/app/app.py +++ b/labapp/app/app.py @@ -3,12 +3,16 @@ """ import os import re +import json +import requests +import base64 +import urllib from flask import Flask, render_template, jsonify, request, redirect, make_response, flash, url_for from flask_caching import Cache -import requests import markdown from ce import get_ce_info, get_ce_state from fetch import get_runner_session, cloudapp_fetch, cloudapp_req_headers, cloudapp_res_headers +from score import score_get_results, score_build_table app = Flask(__name__) app.config['ce_info'] = None @@ -16,10 +20,13 @@ if os.getenv('UDF', None): app.config['ce_info'] = get_ce_info() app.config['UDF'] = True + app.config['SESSION_COOKIE_SECURE'] = True app.config['base_url'] = "mcn-lab.f5demos.com" app.config['CACHE_TYPE'] = 'SimpleCache' cache = Cache(app) app.secret_key = "blahblahblah" +data_cookie = "mcnp-ac-data" +cookie_age = 86400 session = get_runner_session() session.headers.update({"User-Agent": "MCN-Lab-Runner/1.0"}) @@ -42,20 +49,75 @@ def validate_eph_ns(input_name): pattern = r'^[a-zA-Z]+-[a-zA-Z]+$' return bool(re.match(pattern, input_name)) -def eph_ns() -> str: +def get_eph_ns() -> str: """check if ephemeral namespace is set""" - this_eph_ns = request.cookies.get('eph_ns', None) - return this_eph_ns + try: + cookie_b64 = request.cookies.get(data_cookie, None) + if cookie_b64: + return get_cookie_prop(cookie_b64, 'eph_ns') + except Exception: + print("Error getting ephemeral NS") + return None +def get_site() -> str: + """check if ephemeral namespace is set""" + if app.config['ce_info']: + return app.config['ce_info'].get("site_name", None) + return None + +def update_cookie_prop(cookie_b64, prop, value): + """Update a property in a base64 encoded JSON cookie.""" + try: + json_bytes = base64.b64decode(cookie_b64) + json_str = json_bytes.decode('utf-8') + cookie_data = json.loads(json_str) + if not isinstance(cookie_data, dict): + raise ValueError("Cookie data is not a dictionary.") + cookie_data[prop] = value + updated = json.dumps(cookie_data) + base64_bytes = base64.b64encode(updated.encode('utf-8')) + return base64_bytes.decode('utf-8') + except json.JSONDecodeError: + print("Error decoding JSON from cookie.") + """TBD: this is not what we want.""" + return encode_data({}) + except Exception as e: + print(f"An error occurred: {e}") + return encode_data({}) + +def get_cookie_prop(cookie_b64, prop): + """get a cookie prop""" + try: + json_bytes = base64.b64decode(cookie_b64) + json_str = json_bytes.decode('utf-8') + c_dict = json.loads(json_str) + return c_dict[prop] + except json.JSONDecodeError: + print("Error decoding cookie data") + return None + +def encode_data(data): + """Encode dictionary to Base64-encoded JSON.""" + json_str = json.dumps(data) + base64_bytes = base64.b64encode(json_str.encode('utf-8')) + return base64_bytes.decode('utf-8') + +def decode_data(encoded_data): + """Decode Base64-encoded JSON to dictionary.""" + json_bytes = base64.b64decode(encoded_data) + json_str = json_bytes.decode('utf-8') + return json.loads(json_str) + @app.errorhandler(404) @app.errorhandler(500) def return_err(err): """common error handler""" - img = { - 404: "/static/404.png", - 500: "/static/500.png" - } - return render_template("error.html", err_img=img[err.code]) + return render_template("error.html", code=err.code) + +@app.route('/cookie') +def cookie_err(): + """cookie error""" + return render_template("cookie.html") @app.after_request def cache_control(response): @@ -63,29 +125,36 @@ def cache_control(response): if request.path.startswith("/static/") and request.path.endswith(".png"): response.headers['Cache-Control'] = 'public, max-age=3600' return response + +@app.before_request +def ensure_cookie(): + """Ensure that the cookie is present, otherwise redirect to the cookie page.""" + if not request.path.startswith('/static'): + if (request.path not in ['/', '/cookie', '/_ce_status']) and (data_cookie not in request.cookies): + return redirect('/cookie') @app.route('/') def index(): """index page""" - html = render_md("markdown/welcome.md") - return render_template('standard.html', - title="MCN Practical: Overview", - content=html + html = render_template('welcome.html', + title="MCN Practical: Welcome" ) + response = make_response(html) + if data_cookie not in request.cookies: + response.set_cookie(data_cookie, encode_data({}), max_age=cookie_age) + return response @app.route('/overview') -def arch(): - """arch page""" - html = render_md("markdown/overview.md") - return render_template('standard.html', - title="MCN Practical: Architecture", - content=html +def overview(): + """overview page""" + return render_template('overview.html', + title="MCN Practical: Overview" ) @app.route('/setup', methods=['GET', 'POST']) def setup(): """setup page""" - ns = eph_ns() + ns = get_eph_ns() if request.method == 'POST': action = request.form['action'] if action == 'save': @@ -94,117 +163,114 @@ def setup(): flash("Invalid ephemeral namespace.", "danger") return redirect(url_for('setup')) response = make_response(redirect('/setup')) - response.set_cookie('eph_ns', this_eph_ns, max_age=60*60*24) + cookie_b64 = request.cookies.get('mcnp-ac-data', encode_data({})) + cookie_data = update_cookie_prop(cookie_b64, 'eph_ns', this_eph_ns) + response.set_cookie(data_cookie, cookie_data) flash('Ephemeral namespace successfully set.', "success") return response if action == 'clear': response = make_response(redirect('/setup')) - response.set_cookie('eph_ns', '', expires=0) + cookie_b64 = request.cookies.get('mcnp-ac-data', encode_data({})) + cookie_data = update_cookie_prop(cookie_b64, 'eph_ns', None) + response.set_cookie(data_cookie, cookie_data) flash("Ephemeral namespace cleared.", "info") return response - html = render_md("markdown/setup.md") return render_template('setup.html', title="MCN Practical: Setup", - content=html, ns=ns ) -@app.route('/_ce_status') -@cache.cached(timeout=30) -def ce_state(): - """get ce state (internal route)""" - data = get_ce_state(app.config['ce_info']) - return data - -@app.route('/lb') +@app.route('/loadbalancing') def lb(): """lb page""" - ns = eph_ns() - html = render_md("markdown/lb.md") - return render_template('exercise_standard.html', + ns = get_eph_ns() + site = get_site() + return render_template('loadbalancing.html', title="MCN Practical: LB", - content=html, + site=site, ns=ns ) @app.route('/route') def path(): """routing page""" - ns = eph_ns() - html = render_md("markdown/route.md") - return render_template('exercise_standard.html', + ns = get_eph_ns() + return render_template('route.html', title="MCN Practical: HTTP Routing", - content=html, - ns=ns, - + ns=ns ) @app.route('/manipulation') def header(): """manipulation page""" - ns = eph_ns() - html = render_md("markdown/manipulation.md") - return render_template('exercise_standard.html', + ns = get_eph_ns() + site = get_site() + return render_template('manipulation.html', title="MCN Practical: Manipulation", - content=html, - ns=ns + ns=ns, + site=site ) @app.route('/portability') def port(): """portability page""" - ns = eph_ns() - html = render_md("markdown/portability.md") - return render_template('exercise_standard.html', + ns = get_eph_ns() + return render_template('portability.html', title="MCN Practical: Portability", - content=html, - ns=ns - ) - -@app.route('/vnet') -def vnet(): - """vnet page""" - ns = eph_ns() - html = render_md("markdown/reference.md") - return render_template('coming-soon.html', - title="MCN Practical: Reference", - content=html, ns=ns ) -@app.route('/netpolicy') -def netp(): - """netpolicy page""" - ns = eph_ns() - html = render_md("markdown/reference.md") - return render_template('coming-soon.html', - title="MCN Practical: Reference", - content=html, - ns=ns - ) - -@app.route('/ref') +@app.route('/reference') def ref(): """reference page""" - ns = eph_ns() - html = render_md("markdown/reference.md") + ns = get_eph_ns() return render_template('coming-soon.html', - title="MCN Practical: Reference", - content=html, - ns=ns + title="MCN Practical: Reference" ) @app.route('/score') def score(): """scoreboard page""" - ns = eph_ns() - html = render_md("markdown/score.md") - return render_template('coming-soon.html', - title="MCN Practical: Scoreboard", - content=html, + try: + cookie_b64 = request.cookies.get(data_cookie) + this_score = get_cookie_prop(cookie_b64, 'score') + """raise a LabException""" + except Exception: + print("Error getting score") + this_score = {} + try: + p_score = score_get_results(this_score) + over_table = score_build_table(p_score, 'overview', 'Overview') + lb_table = score_build_table(p_score, 'lb', 'Load Balancing') + route_table = score_build_table(p_score, 'route', 'Routing') + manip_table = score_build_table(p_score, 'manip', 'Manipulation') + port_table = score_build_table(p_score, 'port', 'Portability') + except LabException as e: + print(f"Couldn't build score table: {e}") + return render_template('score.html', + title="MCN Practical: Scoreboard", + over_table=over_table, + lb_table=lb_table, + route_table=route_table, + manip_table=manip_table, + port_table=port_table, + ) + +@app.route('/test') +def test(): + """test page""" + ns = get_eph_ns() + return render_template('test.html', + title="MCN Practical: Test", ns=ns ) +@app.route('/_ce_status') +def ce_state(): + """get ce state (internal route)""" + data = get_ce_state(app.config['ce_info']) + return data + @app.route('/_test1') def ex_test(): """Example test""" @@ -234,7 +300,7 @@ def ex_test2(): def lb_aws(): """AWS LB test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") url = f"https://{ns}.{app.config['base_url']}" @@ -249,7 +315,7 @@ def lb_aws(): def lb_azure(): """Azure LB test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") url = f"https://{ns}.{app.config['base_url']}" @@ -269,7 +335,7 @@ def lb_azure(): def route1(): """First Route Test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") base_url = app.config['base_url'] @@ -291,7 +357,7 @@ def route1(): def route2(): """First Route Test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") base_url = app.config['base_url'] @@ -315,7 +381,7 @@ def route2(): def manip1(): """First Manip Test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") base_url = app.config['base_url'] @@ -331,12 +397,13 @@ def manip1(): def manip2(): """Second Manip Test""" try: - ns = eph_ns() + ns = get_eph_ns() + site = get_site() if not ns: raise LabException("Ephemeral NS not set") base_url = app.config['base_url'] url = f"https://{ns}.{base_url}/" - t_headers = { "x-mcn-namespace": ns, "x-mcn-src-site": app.config["ce_info"]["site_name"]} + t_headers = { "x-mcn-namespace": ns, "x-mcn-src-site": site} r_data = cloudapp_req_headers(session, url, 7, t_headers) return jsonify(status='success', data=r_data) except (LabException, ValueError) as e: @@ -348,7 +415,7 @@ def manip2(): def manip3(): """Third Manip Test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") base_url = app.config['base_url'] @@ -372,7 +439,7 @@ def manip3(): def port1(): """Friend test""" try: - ns = eph_ns() + ns = get_eph_ns() if not ns: raise LabException("Ephemeral NS not set") url = f"https://{ns}.{app.config['base_url']}/" @@ -388,7 +455,6 @@ def port2(): """Friend test""" try: data = request.get_json() - print(data) eph_ns = data['userInput'] url = f"https://{eph_ns}.{app.config['base_url']}/" data = cloudapp_fetch(session, url, 7, 'info', {"method": "GET", "path": "/"}) diff --git a/labapp/app/markdown/lb.md b/labapp/app/markdown/lb.md deleted file mode 100644 index 96c0d4e..0000000 --- a/labapp/app/markdown/lb.md +++ /dev/null @@ -1,168 +0,0 @@ - - -# **Load Balancing** - - - -Load balancing is the cornerstone of XC's App Connect functionality. -L7 MCN requires discovering services at one site and making those services available to another. -XC implements this functionality with origin pools and load balancers. -More complicated configurations (underlay networking, security services, observability, etc.) are built on these primitives. - -
- -### **Exercise 1: AWS Cloud App** - -For the initial exercise, make the cloud application running in AWS available to the UDF environment. -Build an origin pool and load balancer based on the exercise requirements. - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/ HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "env": "AWS", - ... -} -``` - -
- -
-
- - -
- -Since this is the first exercise, here are some hints (if you need them). - -
-

- - - -

-
- -
-
- temp - temp -
-
- - -
-
- temp -
-
- - -
-
- temp -
-
- -
-
- - - -### **Exercise 2: Azure Cloud App** - -For the second exercise, make the cloud application running in Azure available to the UDF environment. -Create a new origin pool for the Azure cloud app. Reuse your load balancer. - - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/ HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "env": "Azure", - ... -} -``` - - - -
- -
-
- - - - - - diff --git a/labapp/app/markdown/manipulation.md b/labapp/app/markdown/manipulation.md deleted file mode 100644 index 3c3d09e..0000000 --- a/labapp/app/markdown/manipulation.md +++ /dev/null @@ -1,183 +0,0 @@ - - -# **Manipulation** - - - -Since web traffic has been traversing proxies, engineers have needed to alter HTTP content for increased observability (XFF), performance (Cache-Control), or core functionality (JWT). -"Proxy Pass" functionality has been part of web servers since the early Apache days. -Adding, removing, and altering Headers are tablestakes for ADCs, CDNs, and software-based load balancers. -F5 XC App Connect enables this functionality granularly on routes or broadly on the load balancer. - -
- -### **Exercise 1: Path Rewrite** - -Configure a path prefix rewrite to remove part of the request path when routing to an origin. - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/aws/raw HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "info": { - "path": "/raw" - } - ... -} -``` - -
- -
-
- - -Questions on this functionality are often asked on F5 DevCentral. Here's a hint. - -
-

- - -

-
-
- temp -
-
-
- temp -
-
-
-
- -
- -### **Exercise 2: Request Header Shenanigans** - -While blind header insertion or deletion is useful in some use cases, this exercise focuses on context aware header manipulation. -Use the XC Header Processing docs for reference. - -
- - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/ HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - ... - "request_headers": { - "x-mcn-namespace": "wiggly-yellowtail", - "x-mcn-src-site": "cluster-xxxxxxxx", - }, - ... -} -``` - -
- -
-
- - -
- -### **Exercise 3: Response Header Shenanigans** - -
- - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/aws HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "x-mcn-dest-site": "student-awsnet" -} -``` - -```http -GET https://eph-ns.mcn-lab.f5demos.com/azure HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "x-mcn-dest-site": "student-azurenet" -} -``` - - - -
- -
-
- - - - - - - diff --git a/labapp/app/markdown/overview.md b/labapp/app/markdown/overview.md deleted file mode 100644 index 48a3bd6..0000000 --- a/labapp/app/markdown/overview.md +++ /dev/null @@ -1,172 +0,0 @@ - - -# **Overview** - - - -The lab environment, the service endpoints, and how you interact with the load balancer have been simplified in an effort to focus on concepts. -Understanding the environment, it's topology, and the rudimentary functionality of the cloud app will help in completing the exercises. - -
- -## **Architecture** - -The lab environment contains three distributed sites meshed using the F5 Distributed Cloud Global Network. - - - -
- -Arch diagram - -## **Cloud App** - -An instance of the cloud app is hosted in each remote cloud environment. -The cloud app is a simple application that echoes back an HTTP request. -While working through the lab, unless otherwise noted, test results display headers and info **from the request received by the app**. - -For testing, you can access an endpoint of each cloud app from your browser. - -

- - - - - -

- -
- -## **Lab Exercises** - -Lab exercises will ask you to create configuration in the lab tenant. -Exercise requirements are listed in a table along with an object type indicator. - - - -
- -#### **Test Criteria** - -To complete lab exercises, you will run tests against the load balancer advertised from the Customer Edge in your UDF site. -You will build this load balancer in the first exercise. -All tests will be run from this lab app. - -Each test will specify success criteria immediately before the button. - -Here are some examples to try. - -```http -GET https://foo.mcn-lab.f5demos.com/ HTTP/1.1 - -{ - "info": { - "foo": True - } -} -``` - -
- -
-
- - -The test made a request to https://foo.f5demos.com. -The test succeeded because the response contained the ``JSON`` string ``{ "info": { "foo": True }}``. - -
- -```http -GET https://bar.mcn-lab.f5demos.com/ HTTP/1.1 - -{ - "info": { - "bar": True - } -} -``` - -
- -
-
- - -The test made a request to https://bar.f5demos.com. -The test failed because the response did not contain the ``JSON`` string ``{ "info": { "bar": True}}``. - - -
- -#### **Other Tools** - -``curl`` and ``jq`` are provided on the UDF Runner instance. - -```shell -ubuntu@ubuntu:~$ curl -s https://foo.mcn-lab.f5demos.com/ | jq -{ - "info": "bar" -} -``` - - -
- -## **Issues** - -Use the lab repository issue tracker to report bugs, typos, or lab enhancements. - - - - - - - - - - diff --git a/labapp/app/markdown/portability.md b/labapp/app/markdown/portability.md deleted file mode 100644 index 5bf0541..0000000 --- a/labapp/app/markdown/portability.md +++ /dev/null @@ -1,123 +0,0 @@ - - -# **Portability** - - - - -The configuration built so far handles load balancing, routing, and content manipulation. -XC refers to this object as a "load balancer" -- but it's really the holistic representation of an application whose service endpoints live across the distributed network. -The object is simple -- it doesn't yet include configuration for WAAP, API protection, or a service policy. - -What seperates XC from traditional ADCs is flexibility in defining where a load balancer is advertised. - -
- -### **Exercise 1: Advertise Policy** - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/ HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "info": { - "path": "/" - } - ... -} -``` - -
- -
-
- - - -
-

- -

-
-
- temp - temp -
-
-
- -
- -### **Exercise 2: Find a Friend** - -Do you have a friend working on the lab? -Have they updated their advertise policy to use the virtual site? -Find thier ephemeral namespace (or use the one provided). - - - -
- -#### **Test Criteria** - -```http -GET https://friend-eph-ns.mcn-lab.f5demos.com/ HTTP/1.1 -Host: friend-eph-ns.mcn-lab.f5demos.com - -{ - "info": { - "path": "/" - } - ... -} -``` - -
-
-
-
- - -
-
-
-
-
- - - - - \ No newline at end of file diff --git a/labapp/app/markdown/reference.md b/labapp/app/markdown/reference.md deleted file mode 100644 index e69de29..0000000 diff --git a/labapp/app/markdown/route.md b/labapp/app/markdown/route.md deleted file mode 100644 index 65545c9..0000000 --- a/labapp/app/markdown/route.md +++ /dev/null @@ -1,132 +0,0 @@ - - -# **HTTP Routing** - - - -Modern applications, and some classic ones, are often comprised of disparate services spread across sites. -MCN solutions must be able to make routing decisions based on characterstics of an HTTP request. -F5 XC App Connect is a distributed L7 proxy that provides intelligent routing, visibility, and strategic points of control. - -
- -### **Exercise 1: Path Routing** - -Build routing rules and configure your load balancer to route traffic between the two cloud apps based on the request url. - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/aws/raw HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "env": "aws", - ... -} -``` - -```http -GET https://eph-ns.mcn-lab.f5demos.com/azure/raw HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com - -{ - "env": "azure", - ... -} -``` - -
- -
- -
-
- - - - -### **Exercise 2: Header Routing** - -Build rules to route traffic between the two cloud apps based on an arbitrary HTTP request header. - - - - -
- -#### **Test Criteria** - -```http -GET https://eph-ns.mcn-lab.f5demos.com/raw HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com -X-MCN-lab: aws - -{ - "env": "aws", - ... -} -``` - -```http -GET https://eph-ns.mcn-lab.f5demos.com/raw HTTP/1.1 -Host: eph-ns.mcn-lab.f5demos.com -X-MCN-lab: azure - -{ - "env": "azure", - ... -} -``` - -
- -
-
- - - - - \ No newline at end of file diff --git a/labapp/app/markdown/score.md b/labapp/app/markdown/score.md deleted file mode 100644 index e69de29..0000000 diff --git a/labapp/app/markdown/setup.md b/labapp/app/markdown/setup.md deleted file mode 100644 index 58a0aaa..0000000 --- a/labapp/app/markdown/setup.md +++ /dev/null @@ -1,40 +0,0 @@ - - -# **Setup** - - - -Log in to the lab tenant and open any namespaced tile -- Multi-Cloud App Connect, Distributed Apps, etc. The ephemeral namespace is a randomly generated concatenation of adjective-animal in the navigation bar towards the top. - -eph-ns - - -The ephemeral namespace will be used to derive a unique URL for the load balancer used in the lab exercises. - - Cookies must be allowed for this site. - -
-
- - -
- - -
- - - diff --git a/labapp/app/markdown/welcome.md b/labapp/app/markdown/welcome.md deleted file mode 100644 index aa8ef9e..0000000 --- a/labapp/app/markdown/welcome.md +++ /dev/null @@ -1,81 +0,0 @@ -
- - -# **Welcome** - - - -This lab is a practical training activity. -Each exercise will ask you to configure F5 Distributed Cloud ("XC") objects to reinforce core XC Multi-Cloud Networking ("MCN") App Connect concepts. -Once configured, you will test the configuration using this web application. - -
- -## **Getting Started** - -When your UDF deployment launched, two automated processes started - Customer Edge ("CE") registration and account provisioning in the lab tenant. - -
- -### **Customer Edge** - -The CE in the UDF deployment will register with the lab tenant. -CEs on first launch update software and, often, their OS. This can take ~20 min from when the CE is booted. - -When the CE is ready, the status indicator in the navigation pane (👀 look to the left) will show . -Use the indicator to find the CE site name needed for configuring the load balancer's advertise policy. - -
- -### **Account Provisioning** - -Check the email used to launch your UDF deployment for a welcome or password reset email from the lab tenant. -Update your password to log into the tenant. - -

- - - - -

- -
- -### **While You Wait** - -Here's a few things you can do while waiting for the CE to be registered and provisioned: - - - - - - - - - diff --git a/labapp/app/reference.html b/labapp/app/reference.html new file mode 100644 index 0000000..30de113 --- /dev/null +++ b/labapp/app/reference.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +

+ F5 Distributed Cloud documentation blurb +

+ +

Load Balancing

+ +

Routing

+ +

Content Manipulation

+ +

Portability

+ +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/score.py b/labapp/app/score.py new file mode 100644 index 0000000..085f189 --- /dev/null +++ b/labapp/app/score.py @@ -0,0 +1,124 @@ +""" +Build Scoreboards +""" +import copy + +score_schema = { + "/_test1": { + "name": "Foo Test Example", + "key": "overview", + "href": "/overview", + "result": "none", + }, + "/_test2": { + "name": "Bar Test Example", + "key": "overview", + "href": "/overview", + "result": "none" + }, + "/_lb1": { + "name": "AWS Cloud App", + "key": "lb", + "href": "/loadbalancing", + "result": "none" + }, + "/_lb2": { + "name": "Azure Cloud App", + "key": "lb", + "href": "/loadbalancing", + "result": "none" + }, + "/_route1": { + "name": "Path Routing", + "key": "route", + "href": "/route", + "result": "none" + }, + "/_route2": { + "name": "Header Routing", + "key": "route", + "href": "/route", + "result": "none" + }, + "/_manip1": { + "name": "Path Rewrite", + "key": "manip", + "href": "/manipulation", + "result": "none" + }, + "/_manip2": { + "name": "Request Headers", + "key": "manip", + "href": "/manipulation", + "result": "none" + }, + "/_manip3": { + "name": "Response Headers", + "key": "manip", + "href": "/manipulation", + "result": "none" + }, + "/_port1": { + "name": "Advertise Policy", + "key": "port", + "href": "/portability", + "result": "none" + }, + "/_port2": { + "name": "Find a Friend", + "key": "port", + "href": "/portability", + "result": "none" + } +} + +result_map = { + "fail": '', + "pass": '', + "none": '' +} + +def score_get_results(cookie_results): + """ + build score result from cookie + """ + this_score = copy.deepcopy(score_schema) + for test_path, result in cookie_results.items(): + if test_path in score_schema: + this_score[test_path]['result'] = result + return this_score + +def score_sort(scores, key): + """sort score by key""" + scores = {k: v for k, v in scores.items() if v['key'] == key} + return scores + +def score_build_table(scores, section, name): + """build table section""" + section_scores = score_sort(scores, section) + rows_html = "" + for key, score in section_scores.items(): + r_icon = result_map[score['result']] + section_html = f""" + + {score['name']} + {r_icon} + + """ + rows_html += section_html + html = f""" + + + + + + + + {rows_html} + +
{name}
+ """ + return html + + + diff --git a/labapp/app/static/404.png b/labapp/app/static/404.png deleted file mode 100644 index d88f9a7..0000000 Binary files a/labapp/app/static/404.png and /dev/null differ diff --git a/labapp/app/static/500.png b/labapp/app/static/500.png deleted file mode 100644 index 50460bf..0000000 Binary files a/labapp/app/static/500.png and /dev/null differ diff --git a/labapp/app/static/arch-base.png b/labapp/app/static/arch-base.png deleted file mode 100644 index 1ea20c0..0000000 Binary files a/labapp/app/static/arch-base.png and /dev/null differ diff --git a/labapp/app/static/arch.png b/labapp/app/static/arch.png deleted file mode 100644 index 8c8e283..0000000 Binary files a/labapp/app/static/arch.png and /dev/null differ diff --git a/labapp/app/static/ce.png b/labapp/app/static/ce.png deleted file mode 100644 index 867fd73..0000000 Binary files a/labapp/app/static/ce.png and /dev/null differ diff --git a/labapp/app/static/coming-soon.png b/labapp/app/static/coming-soon.png deleted file mode 100644 index 7bd491a..0000000 Binary files a/labapp/app/static/coming-soon.png and /dev/null differ diff --git a/labapp/app/static/custom.css b/labapp/app/static/custom.css index 6d11d83..ed25e3a 100644 --- a/labapp/app/static/custom.css +++ b/labapp/app/static/custom.css @@ -18,6 +18,62 @@ main { overflow-y: auto; } +.full-height-container { + min-height: 100vh; /* Set the minimum height to 100% of the viewport height */ +} + +.lab-icon { + height: 30px; + width: auto; + padding-right: 25px; +} + +.lab-test-button { + width: 175px; +} + +.lab-post-button { + width: 200px; +} + +.lab-ex-test { + width: 125px; +} + +.lab-image-container { + height: 200px; /* Fixed height */ + overflow: hidden; /* Ensures nothing extends outside the container */ + display: flex; /* Flexbox to help with centering/positioning the image */ + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically, if needed */ +} + +.lab-header { + border-radius: .8em; + filter: brightness(110%) contrast(80%); + filter: blur(.1px); +} + +.lab-header-clear { + border-radius: .8em; +} +/* Testing */ + +.sidebar-link { + display: block; + padding: 8px 16px; + color: #333; /* Adjust the color to fit your theme */ + text-decoration: none; /* Remove underline from links */ + border-radius: 4px; /* Rounded corners for links */ +} + +.sidebar-link:hover { + background-color: #f8f9fa; /* Light grey background on hover */ + color: #0056b3; /* Change color on hover for better visibility */ +} + +/* Testing */ + .row { height: 100%; } @@ -43,18 +99,22 @@ main { padding: 50px; } -.err { +.main-body { + padding: 50px; +} + +.err-container { display: flex; - justify-content: center; /* Centers horizontally */ - align-items: center; /* Centers vertically */ + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ height: 100vh; /* Full viewport height */ + margin-bottom: 0 } - -.err img { - display: block; - width: 100%; /* This will make the image responsive */ - max-width: 600px; /* Maximum width of the image */ - margin: 0 auto; /* Center the image */ +.err-container img { + width: 80%; + max-width: 100%; + height: auto; /* Maintain aspect ratio */ + border-radius: .8em; } .err p { diff --git a/labapp/app/static/dracula.css b/labapp/app/static/dracula.css deleted file mode 100644 index 087149f..0000000 --- a/labapp/app/static/dracula.css +++ /dev/null @@ -1,81 +0,0 @@ -/* Dracula Theme v1.2.5 - * - * https://github.com/dracula/highlightjs - * - * Copyright 2016-present, All rights reserved - * - * Code licensed under the MIT license - * - * @author Denis Ciccale - * @author Zeno Rocha - */ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #282a36; -} - -.hljs-built_in, -.hljs-selector-tag, -.hljs-section, -.hljs-link { - color: #8be9fd; -} - -.hljs-keyword { - color: #ff79c6; -} - -.hljs, -.hljs-subst { - color: #f8f8f2; -} - -.hljs-title, -.hljs-attr, -.hljs-meta-keyword { - font-style: italic; - color: #50fa7b; -} - -.hljs-string, -.hljs-meta, -.hljs-name, -.hljs-type, -.hljs-symbol, -.hljs-bullet, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #f1fa8c; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion { - color: #6272a4; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-title, -.hljs-section, -.hljs-doctag, -.hljs-type, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-literal, -.hljs-number { - color: #bd93f9; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/labapp/app/static/helpers.js b/labapp/app/static/helpers.js index e39aac5..3c0cc14 100644 --- a/labapp/app/static/helpers.js +++ b/labapp/app/static/helpers.js @@ -1,64 +1,119 @@ +// Utility function to get a cookie by name +function getCookie(name) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) return parts.pop().split(';').shift(); + return null; +} + +// Utility function to set a cookie function setCookie(name, value, days) { - var expires = ""; + let expires = ''; if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days*24*60*60*1000)); - expires = "; expires=" + date.toUTCString(); + const date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = `; expires=${date.toUTCString()}`; } - document.cookie = name + "=" + (value || "") + expires + "; path=/"; + document.cookie = `${name}=${value}${expires}; path=/`; } -function getCookie(name) { - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for(var i=0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1,c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); +function updateScore(requestUrl, status) { + // Retrieve the current cookie, assume it's base64 encoded, or default to an encoded empty object + const base64EncodedData = getCookie('mcnp-ac-data') || btoa('{}'); + const cookieStr = atob(base64EncodedData); + let cookieData = JSON.parse(cookieStr); + + // Check if the 'score' object exists, if not initialize it + if (!cookieData.score) { + cookieData.score = {}; } - return null; + + // Update the score object with the result of the current request + cookieData.score[requestUrl] = status; + + // Convert the updated cookie object back to string, then encode to base64 + const updatedStr = JSON.stringify(cookieData); + const updatedBase64Data = btoa(updatedStr); + + // Update the cookie with the new base64 encoded data + setCookie('mcnp-ac-data', updatedBase64Data, 1); +} + +function clearScore(requestUrl, status) { + // Retrieve the current cookie, assume it's base64 encoded, or default to an encoded empty object + const base64EncodedData = getCookie('mcnp-ac-data') || btoa('{}'); + const cookieStr = atob(base64EncodedData); + let cookieData = JSON.parse(cookieStr); + + // Clear the score + cookieData.score = {}; + + // Convert the updated cookie object back to string, then encode to base64 + const updatedStr = JSON.stringify(cookieData); + const updatedBase64Data = btoa(updatedStr); + + // Update the cookie with the new base64 encoded data + setCookie('mcnp-ac-data', updatedBase64Data, 1); } -async function makeHttpRequest(buttonId, requestUrl, resultDivId) { - const button = document.getElementById(buttonId); - const resultDiv = document.getElementById(resultDivId); - button.disabled = true; - try { - const response = await axios.get(requestUrl); - if (response.data.status === 'success') { - const prettyJson = JSON.stringify(response.data.data, null, 4); - resultDiv.innerHTML = `
Request Succeeded:
${prettyJson}
`; - } else { - const errJson = JSON.stringify(response.data.error, null, 4); - resultDiv.innerHTML = `
Request Failed:
${errJson}
`; - } - } catch (error) { - resultDiv.innerHTML = `
Error: ${error.message}
`; - } finally { - button.disabled = false; - resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } +async function testHttpRequest(buttonId, requestUrl, resultDivId, buttonTxt) { + const button = document.getElementById(buttonId); + const resultDiv = document.getElementById(resultDivId); + + // Add spinner to button and disable it + button.innerHTML = `Testing...`; + button.disabled = true; + + try { + const response = await axios.get(requestUrl); + if (response.data.status === 'success') { + const prettyJson = JSON.stringify(response.data.data, null, 4); + resultDiv.innerHTML = `
Request Succeeded:
${prettyJson}
`; + updateScore(requestUrl, 'pass'); + } else { + const errJson = JSON.stringify(response.data.error, null, 4); + resultDiv.innerHTML = `
Request Failed:
${errJson}
`; + updateScore(requestUrl, 'fail'); + } + } catch (error) { + resultDiv.innerHTML = `
Error: ${error.message}
`; + updateScore(requestUrl, 'fail'); + } finally { + // Restore original button text and remove spinner + button.innerHTML = buttonTxt; + button.disabled = false; + resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } } -async function makePostRequest(buttonId, requestUrl, resultDivId, inputDataId) { +async function testPostRequest(buttonId, requestUrl, resultDivId, inputDataId, buttonTxt) { const button = document.getElementById(buttonId); const resultDiv = document.getElementById(resultDivId); const inputData = document.getElementById(inputDataId).value; + + + // Add spinner and change button text + button.innerHTML = `Testing...`; button.disabled = true; try { const response = await axios.post(requestUrl, { userInput: inputData }); if (response.data.status === 'success') { const prettyJson = JSON.stringify(response.data.data, null, 4); - resultDiv.innerHTML = `
Request Succeeded:
${prettyJson}
`; + resultDiv.innerHTML = `
Request Succeeded:
${prettyJson}
`; + updateScore(requestUrl, 'pass'); } else { const errJson = JSON.stringify(response.data.error, null, 4); - resultDiv.innerHTML = `
Request Failed:
${errJson}
`; + resultDiv.innerHTML = `
Request Failed:
${errJson}
`; + updateScore(requestUrl, 'fail'); } } catch (error) { resultDiv.innerHTML = `
Error: ${error.message}
`; + updateScore(requestUrl, 'fail'); } finally { + // Restore original button text + button.innerHTML = buttonTxt; button.disabled = false; resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } -} +} \ No newline at end of file diff --git a/labapp/app/static/images/banners/404-err.png b/labapp/app/static/images/banners/404-err.png new file mode 100644 index 0000000..4b3e875 Binary files /dev/null and b/labapp/app/static/images/banners/404-err.png differ diff --git a/labapp/app/static/images/banners/500-err.png b/labapp/app/static/images/banners/500-err.png new file mode 100644 index 0000000..9ec8cc9 Binary files /dev/null and b/labapp/app/static/images/banners/500-err.png differ diff --git a/labapp/app/static/images/banners/coming-soon.png b/labapp/app/static/images/banners/coming-soon.png new file mode 100644 index 0000000..07f24bf Binary files /dev/null and b/labapp/app/static/images/banners/coming-soon.png differ diff --git a/labapp/app/static/images/banners/cookie-err.png b/labapp/app/static/images/banners/cookie-err.png new file mode 100644 index 0000000..ad188e7 Binary files /dev/null and b/labapp/app/static/images/banners/cookie-err.png differ diff --git a/labapp/app/static/images/banners/lb-banner.png b/labapp/app/static/images/banners/lb-banner.png new file mode 100644 index 0000000..8ef6194 Binary files /dev/null and b/labapp/app/static/images/banners/lb-banner.png differ diff --git a/labapp/app/static/images/banners/manip-banner.png b/labapp/app/static/images/banners/manip-banner.png new file mode 100644 index 0000000..8d4143e Binary files /dev/null and b/labapp/app/static/images/banners/manip-banner.png differ diff --git a/labapp/app/static/images/banners/overview-banner.png b/labapp/app/static/images/banners/overview-banner.png new file mode 100644 index 0000000..eb2f68b Binary files /dev/null and b/labapp/app/static/images/banners/overview-banner.png differ diff --git a/labapp/app/static/images/banners/port-banner.png b/labapp/app/static/images/banners/port-banner.png new file mode 100644 index 0000000..c14fefc Binary files /dev/null and b/labapp/app/static/images/banners/port-banner.png differ diff --git a/labapp/app/static/practical.png b/labapp/app/static/images/banners/practical.png similarity index 100% rename from labapp/app/static/practical.png rename to labapp/app/static/images/banners/practical.png diff --git a/labapp/app/static/images/banners/reference-banner.png b/labapp/app/static/images/banners/reference-banner.png new file mode 100644 index 0000000..5a7ab25 Binary files /dev/null and b/labapp/app/static/images/banners/reference-banner.png differ diff --git a/labapp/app/static/images/banners/routing-banner.png b/labapp/app/static/images/banners/routing-banner.png new file mode 100644 index 0000000..0ee6cfa Binary files /dev/null and b/labapp/app/static/images/banners/routing-banner.png differ diff --git a/labapp/app/static/images/banners/score-banner.png b/labapp/app/static/images/banners/score-banner.png new file mode 100644 index 0000000..8daf037 Binary files /dev/null and b/labapp/app/static/images/banners/score-banner.png differ diff --git a/labapp/app/static/images/banners/setup-banner.png b/labapp/app/static/images/banners/setup-banner.png new file mode 100644 index 0000000..7642d7c Binary files /dev/null and b/labapp/app/static/images/banners/setup-banner.png differ diff --git a/labapp/app/static/bad.png b/labapp/app/static/images/common/bad.png similarity index 100% rename from labapp/app/static/bad.png rename to labapp/app/static/images/common/bad.png diff --git a/labapp/app/static/good.png b/labapp/app/static/images/common/good.png similarity index 100% rename from labapp/app/static/good.png rename to labapp/app/static/images/common/good.png diff --git a/labapp/app/static/lb-icon.png b/labapp/app/static/images/common/lb-icon.png similarity index 100% rename from labapp/app/static/lb-icon.png rename to labapp/app/static/images/common/lb-icon.png diff --git a/labapp/app/static/multi-cloud-app-connect.svg b/labapp/app/static/images/common/multi-cloud-app-connect.svg similarity index 100% rename from labapp/app/static/multi-cloud-app-connect.svg rename to labapp/app/static/images/common/multi-cloud-app-connect.svg diff --git a/labapp/app/static/origin-icon.png b/labapp/app/static/images/common/origin-icon.png similarity index 100% rename from labapp/app/static/origin-icon.png rename to labapp/app/static/images/common/origin-icon.png diff --git a/labapp/app/static/route-icon.png b/labapp/app/static/images/common/route-icon.png similarity index 100% rename from labapp/app/static/route-icon.png rename to labapp/app/static/images/common/route-icon.png diff --git a/labapp/app/static/unknown.png b/labapp/app/static/images/common/unknown.png similarity index 100% rename from labapp/app/static/unknown.png rename to labapp/app/static/images/common/unknown.png diff --git a/labapp/app/static/load-balancer1.png b/labapp/app/static/images/lb/load-balancer1.png similarity index 100% rename from labapp/app/static/load-balancer1.png rename to labapp/app/static/images/lb/load-balancer1.png diff --git a/labapp/app/static/load-balancer2.png b/labapp/app/static/images/lb/load-balancer2.png similarity index 100% rename from labapp/app/static/load-balancer2.png rename to labapp/app/static/images/lb/load-balancer2.png diff --git a/labapp/app/static/origin-pool.png b/labapp/app/static/images/lb/origin-pool.png similarity index 100% rename from labapp/app/static/origin-pool.png rename to labapp/app/static/images/lb/origin-pool.png diff --git a/labapp/app/static/origin-server.png b/labapp/app/static/images/lb/origin-server.png similarity index 100% rename from labapp/app/static/origin-server.png rename to labapp/app/static/images/lb/origin-server.png diff --git a/labapp/app/static/rewrite1.png b/labapp/app/static/images/manip/rewrite1.png similarity index 100% rename from labapp/app/static/rewrite1.png rename to labapp/app/static/images/manip/rewrite1.png diff --git a/labapp/app/static/rewrite2.png b/labapp/app/static/images/manip/rewrite2.png similarity index 100% rename from labapp/app/static/rewrite2.png rename to labapp/app/static/images/manip/rewrite2.png diff --git a/labapp/app/static/aws.png b/labapp/app/static/images/overview/aws.png similarity index 100% rename from labapp/app/static/aws.png rename to labapp/app/static/images/overview/aws.png diff --git a/labapp/app/static/azure.png b/labapp/app/static/images/overview/azure.png similarity index 100% rename from labapp/app/static/azure.png rename to labapp/app/static/images/overview/azure.png diff --git a/labapp/app/static/mcn-prac-arch-base.png b/labapp/app/static/images/overview/mcn-prac-arch-base.png similarity index 100% rename from labapp/app/static/mcn-prac-arch-base.png rename to labapp/app/static/images/overview/mcn-prac-arch-base.png diff --git a/labapp/app/static/mcnp-aws.png b/labapp/app/static/images/overview/mcnp-aws.png similarity index 100% rename from labapp/app/static/mcnp-aws.png rename to labapp/app/static/images/overview/mcnp-aws.png diff --git a/labapp/app/static/mcnp-azure.png b/labapp/app/static/images/overview/mcnp-azure.png similarity index 100% rename from labapp/app/static/mcnp-azure.png rename to labapp/app/static/images/overview/mcnp-azure.png diff --git a/labapp/app/static/mcnp-udf.png b/labapp/app/static/images/overview/mcnp-udf.png similarity index 100% rename from labapp/app/static/mcnp-udf.png rename to labapp/app/static/images/overview/mcnp-udf.png diff --git a/labapp/app/static/vsite.png b/labapp/app/static/images/port/vsite.png similarity index 100% rename from labapp/app/static/vsite.png rename to labapp/app/static/images/port/vsite.png diff --git a/labapp/app/static/vsite2.png b/labapp/app/static/images/port/vsite2.png similarity index 100% rename from labapp/app/static/vsite2.png rename to labapp/app/static/images/port/vsite2.png diff --git a/labapp/app/static/images/score/ms-forms.png b/labapp/app/static/images/score/ms-forms.png new file mode 100644 index 0000000..1164aec Binary files /dev/null and b/labapp/app/static/images/score/ms-forms.png differ diff --git a/labapp/app/static/eph-ns.png b/labapp/app/static/images/setup/eph-ns.png similarity index 100% rename from labapp/app/static/eph-ns.png rename to labapp/app/static/images/setup/eph-ns.png diff --git a/labapp/app/static/check.png b/labapp/app/static/images/welcome/check.png similarity index 100% rename from labapp/app/static/check.png rename to labapp/app/static/images/welcome/check.png diff --git a/labapp/app/static/email.png b/labapp/app/static/images/welcome/email.png similarity index 100% rename from labapp/app/static/email.png rename to labapp/app/static/images/welcome/email.png diff --git a/labapp/app/static/password.png b/labapp/app/static/images/welcome/password.png similarity index 100% rename from labapp/app/static/password.png rename to labapp/app/static/images/welcome/password.png diff --git a/labapp/app/static/load-balancing.png b/labapp/app/static/load-balancing.png deleted file mode 100644 index 1fd7284..0000000 Binary files a/labapp/app/static/load-balancing.png and /dev/null differ diff --git a/labapp/app/static/logo.png b/labapp/app/static/logo.png deleted file mode 100644 index e9a66db..0000000 Binary files a/labapp/app/static/logo.png and /dev/null differ diff --git a/labapp/app/static/manip.png b/labapp/app/static/manip.png deleted file mode 100644 index 772b24a..0000000 Binary files a/labapp/app/static/manip.png and /dev/null differ diff --git a/labapp/app/static/network-policy.png b/labapp/app/static/network-policy.png deleted file mode 100644 index d1d0ddc..0000000 Binary files a/labapp/app/static/network-policy.png and /dev/null differ diff --git a/labapp/app/static/new_improved_logo.png b/labapp/app/static/new_improved_logo.png deleted file mode 100644 index a3c00cd..0000000 Binary files a/labapp/app/static/new_improved_logo.png and /dev/null differ diff --git a/labapp/app/static/nord.css b/labapp/app/static/nord.css new file mode 100644 index 0000000..88b406a --- /dev/null +++ b/labapp/app/static/nord.css @@ -0,0 +1,315 @@ +/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +title Nord highlight.js + +project nord-highlightjs + +version 0.1.0 + +repository https://github.com/arcticicestudio/nord-highlightjs + +author Arctic Ice Studio + +email development@arcticicestudio.com + +copyright Copyright (C) 2017 + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +[References] +Nord + https://github.com/arcticicestudio/nord +highlight.js + http://highlightjs.readthedocs.io/en/latest/style-guide.html + http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html +*/ +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #2E3440; +} + +.hljs, +.hljs-subst { + color: #D8DEE9; +} + +.hljs-selector-tag { + color: #81A1C1; +} + +.hljs-selector-id { + color: #8FBCBB; + font-weight: bold; +} + +.hljs-selector-class { + color: #8FBCBB; +} + +.hljs-selector-attr { + color: #8FBCBB; +} + +.hljs-selector-pseudo { + color: #88C0D0; +} + +.hljs-addition { + background-color: rgba(163, 190, 140, 0.5); +} + +.hljs-deletion { + background-color: rgba(191, 97, 106, 0.5); +} + +.hljs-built_in, +.hljs-type { + color: #8FBCBB; +} + +.hljs-class { + color: #8FBCBB; +} + +.hljs-function { + color: #88C0D0; +} + +.hljs-function > .hljs-title { + color: #88C0D0; +} + +.hljs-keyword, +.hljs-literal, +.hljs-symbol { + color: #81A1C1; +} + +.hljs-number { + color: #B48EAD; +} + +.hljs-regexp { + color: #EBCB8B; +} + +.hljs-string { + color: #A3BE8C; +} + +.hljs-title { + color: #8FBCBB; +} + +.hljs-params { + color: #D8DEE9; +} + +.hljs-bullet { + color: #81A1C1; +} + +.hljs-code { + color: #8FBCBB; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-formula { + color: #8FBCBB; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-link:hover { + text-decoration: underline; +} + +.hljs-quote { + color: #4C566A; +} + +.hljs-comment { + color: #4C566A; +} + +.hljs-doctag { + color: #8FBCBB; +} + +.hljs-meta, +.hljs-meta-keyword { + color: #5E81AC; +} + +.hljs-meta-string { + color: #A3BE8C; +} + +.hljs-attr { + color: #8FBCBB; +} + +.hljs-attribute { + color: #D8DEE9; +} + +.hljs-builtin-name { + color: #81A1C1; +} + +.hljs-name { + color: #81A1C1; +} + +.hljs-section { + color: #88C0D0; +} + +.hljs-tag { + color: #81A1C1; +} + +.hljs-variable { + color: #D8DEE9; +} + +.hljs-template-variable { + color: #D8DEE9; +} + +.hljs-template-tag { + color: #5E81AC; +} + +.abnf .hljs-attribute { + color: #88C0D0; +} + +.abnf .hljs-symbol { + color: #EBCB8B; +} + +.apache .hljs-attribute { + color: #88C0D0; +} + +.apache .hljs-section { + color: #81A1C1; +} + +.arduino .hljs-built_in { + color: #88C0D0; +} + +.aspectj .hljs-meta { + color: #D08770; +} + +.aspectj > .hljs-title { + color: #88C0D0; +} + +.bnf .hljs-attribute { + color: #8FBCBB; +} + +.clojure .hljs-name { + color: #88C0D0; +} + +.clojure .hljs-symbol { + color: #EBCB8B; +} + +.coq .hljs-built_in { + color: #88C0D0; +} + +.cpp .hljs-meta-string { + color: #8FBCBB; +} + +.css .hljs-built_in { + color: #88C0D0; +} + +.css .hljs-keyword { + color: #D08770; +} + +.diff .hljs-meta { + color: #8FBCBB; +} + +.ebnf .hljs-attribute { + color: #8FBCBB; +} + +.glsl .hljs-built_in { + color: #88C0D0; +} + +.groovy .hljs-meta:not(:first-child) { + color: #D08770; +} + +.haxe .hljs-meta { + color: #D08770; +} + +.java .hljs-meta { + color: #D08770; +} + +.ldif .hljs-attribute { + color: #8FBCBB; +} + +.lisp .hljs-name { + color: #88C0D0; +} + +.lua .hljs-built_in { + color: #88C0D0; +} + +.moonscript .hljs-built_in { + color: #88C0D0; +} + +.nginx .hljs-attribute { + color: #88C0D0; +} + +.nginx .hljs-section { + color: #5E81AC; +} + +.pf .hljs-built_in { + color: #88C0D0; +} + +.processing .hljs-built_in { + color: #88C0D0; +} + +.scss .hljs-keyword { + color: #81A1C1; +} + +.stylus .hljs-keyword { + color: #81A1C1; +} + +.swift .hljs-meta { + color: #D08770; +} + +.vim .hljs-built_in { + color: #88C0D0; + font-style: italic; +} + +.yaml .hljs-meta { + color: #D08770; +} \ No newline at end of file diff --git a/labapp/app/static/path.png b/labapp/app/static/path.png deleted file mode 100644 index b26d612..0000000 Binary files a/labapp/app/static/path.png and /dev/null differ diff --git a/labapp/app/static/portable.png b/labapp/app/static/portable.png deleted file mode 100644 index a8af9ca..0000000 Binary files a/labapp/app/static/portable.png and /dev/null differ diff --git a/labapp/app/static/setup.png b/labapp/app/static/setup.png deleted file mode 100644 index 87576c5..0000000 Binary files a/labapp/app/static/setup.png and /dev/null differ diff --git a/labapp/app/static/table.css b/labapp/app/static/table.css new file mode 100644 index 0000000..a5983ec --- /dev/null +++ b/labapp/app/static/table.css @@ -0,0 +1,60 @@ +/* General table styling */ +.score-table { + background: #34495E; /* Dark blue background for contrast */ + color: rgb(85, 155, 221); /* Light blue text for readability */ + border-radius: .9em; /* Rounded corners for aesthetics */ + overflow: hidden; /* Keeps the inner elements contained within the border radius */ + margin: 1em 0; /* Spacing above and below each table */ +} + +/* Ensures table cells have appropriate padding and text alignment */ +.score-table th, .score-table td { + text-align: left; /* Align text to the left */ + display: block; /* Stack cells vertically on small screens */ + padding: .4em .7em; /* Padding for spacing within cells */ +} + +.score-table th { + background: #34495E; /* Dark blue background for contrast */ + color: white; +} + +.score-table td { + background: rgb(215, 223, 223); + color: black; +} + +/* Specific styles for pass/fail scenarios */ +.score-pass { + color: rgb(25, 135, 84); /* Green for pass */ +} + +.score-fail { + color: rgb(203, 68, 74); /* Red for fail */ +} + +/* Responsive settings and adjustments for table display */ +@media (min-width: 768px) { /* Adjusting for medium-sized devices and up */ + .score-table { + min-width: 300px; /* Minimum width for tables on larger screens */ + width: 100%; /* Full width to utilize available space */ + } + .score-table th, .score-table td { + display: table-cell; /* Normal table cell display on larger screens */ + } +} + +/* Responsive container to allow horizontal scrolling on small devices */ +.table-responsive { + width: 100%; /* Full width */ + overflow-x: auto; /* Enable horizontal scrolling when necessary */ +} + +/* Adjust first and last cell padding for aesthetics */ +.score-table th:first-child, .score-table td:first-child { + padding-left: .75em; /* Remove left padding for clean alignment */ +} + +.score-table th:last-child, .score-table td:last-child { + padding-right: 0; /* Remove right padding for clean alignment */ +} diff --git a/labapp/app/static/tls-icon.png b/labapp/app/static/tls-icon.png deleted file mode 100644 index 51e2b30..0000000 Binary files a/labapp/app/static/tls-icon.png and /dev/null differ diff --git a/labapp/app/static/virtual-networks.png b/labapp/app/static/virtual-networks.png deleted file mode 100644 index 2d60427..0000000 Binary files a/labapp/app/static/virtual-networks.png and /dev/null differ diff --git a/labapp/app/templates/base.html b/labapp/app/templates/base.html index 3cf5c68..0e7df0f 100644 --- a/labapp/app/templates/base.html +++ b/labapp/app/templates/base.html @@ -10,7 +10,7 @@ - + @@ -21,112 +21,98 @@
-
+
+ + + + + + - -
-
-
{% block content %} diff --git a/labapp/app/templates/coming-soon.html b/labapp/app/templates/coming-soon.html index a9b1fa1..b0c8fbe 100644 --- a/labapp/app/templates/coming-soon.html +++ b/labapp/app/templates/coming-soon.html @@ -3,9 +3,9 @@ {% block title %}{{ title }}{% endblock %} {% block content %} -
- - Coming Soon - +
+
+ Banner
+
{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/cookie.html b/labapp/app/templates/cookie.html new file mode 100644 index 0000000..3901cab --- /dev/null +++ b/labapp/app/templates/cookie.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+
+ Banner + +   Home +
+
+ +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/error.html b/labapp/app/templates/error.html index 284215c..9d75335 100644 --- a/labapp/app/templates/error.html +++ b/labapp/app/templates/error.html @@ -3,9 +3,29 @@ {% block title %}MCN Practical Error{% endblock %} {% block content %} -
- - Descriptive Text - + {% if code == 404 %} +
+
+ Banner +
+ {% endif %} + + {% if code == 500 %} + + + +
+
+ Banner + +
+
+ {% endif %} + {% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/exercise_standard.html b/labapp/app/templates/exercise_standard.html index b2b0b0e..b18fbdc 100644 --- a/labapp/app/templates/exercise_standard.html +++ b/labapp/app/templates/exercise_standard.html @@ -12,7 +12,7 @@ {% endif %}
-
+
{{ content|safe }}
{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/future_base.html b/labapp/app/templates/future_base.html deleted file mode 100644 index e0b9a83..0000000 --- a/labapp/app/templates/future_base.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - {% block title %}{{ title }}{% endblock %} - - - - - - - - - - - - - - - - - -
-
- - - - -
- {% block content %} - {% endblock %} -
-
-
- - - - diff --git a/labapp/app/templates/loadbalancing.html b/labapp/app/templates/loadbalancing.html new file mode 100644 index 0000000..997850b --- /dev/null +++ b/labapp/app/templates/loadbalancing.html @@ -0,0 +1,217 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +
+ {% if not ns %} + + {% endif %} +
+ +

+ Load balancing is the cornerstone of XC's App Connect functionality. + L7 MCN requires discovering services at one site and making those services available to another. + XC implements this functionality with origin pools and load balancers. + More complicated configurations (underlay networking, security services, observability) are built on these primitives. +

+ +
+ +

Exercise 1: AWS Cloud App

+

+ For the initial exercise, make the cloud application running in AWS available to the UDF environment. + Build an origin pool and load balancer based on the exercise requirements. +

+ +
+
    +
  • + + The URL for the cloud app hosted in AWS is https://aws-cloud-app.mcn-lab.f5demos.com +
  • +
  • + + The cloud app is only reachable from the student-awsnet site. +
  • +
  • + + The cloud app is TLS only. +
  • +
  • + + The load balancer domain is {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com. +
  • +
  • + + Use the mcn-lab-wildcard wildcard cert in the shared NS to enable TLS on the LB. +
  • +
  • + + Do not advertise your load balancer to the internet. + {% if site %} Your site name is {{ site }}{% endif %} +
  • +
+
+ +
+ +

Test Criteria

+ +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/ HTTP/1.1
+Host: {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com
+      
+
+ +

+{
+    "env": "AWS",
+    ...
+}
+    
+
+ + +
+ +
+
+ + +
+ +

+ Since this is the first exercise, here are some hints. +

+ + +
+

+ + + +

+
+ +
+
+ temp +
+ temp +
+
+ + +
+
+ temp +
+
+ + +
+
+ temp +
+
+ +
+
+ + + + +

Exercise 2: Azure Cloud App

+ +

+ For the second exercise, make the cloud application running in Azure available to the UDF environment. + Create a new origin pool for the Azure cloud app. Reuse your load balancer. +

+ + +
+
    +
  • + + The URL for the cloud app hosted in Azure is https://azure-cloud-app.mcn-lab.f5demos.com +
  • +
  • + + The cloud app is only reachable from the student-azurenet site. +
  • +
  • + + The cloud app is TLS only. +
  • +
  • + + Keep the AWS Origin Pool in place. +
  • +
+
+ + +
+ + +

Test Criteria

+ +

+GET https://{{ ns }}.mcn-lab.f5demos.com/ HTTP/1.1
+Host: {{ ns }}.mcn-lab.f5demos.com
+      
+
+ +

+{
+    "env": "Azure",
+    ...
+}
+        
+
+ + + + +
+ +
+
+ + + + + + + + + +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/manipulation.html b/labapp/app/templates/manipulation.html new file mode 100644 index 0000000..2738bb9 --- /dev/null +++ b/labapp/app/templates/manipulation.html @@ -0,0 +1,235 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +
+ {% if not ns %} + + {% endif %} +
+ +

+ Since web traffic has been traversing proxies, engineers have needed to alter HTTP content for increased observability (XFF), performance (Cache-Control), or other core functionality (JWT). + "Proxy Pass" has been part of web servers since the early Apache days. + Adding, removing, and altering headers are tablestakes for ADCs, CDNs, and software-based load balancers. + F5 XC App Connect implements these features granularly on routes or more broadly on the load balancer. +

+ +
+ +

Exercise 1: Path Rewrite

+ +

+Configure a path prefix rewrite to remove part of the request path when routing to an origin. +

+ +
+
    +
  • + + Keep your configuration from the previous exercise in place. +
  • +
  • + + Requests to https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/aws/raw/ need to arrive at the origin with a path of /raw. +
  • +
+
+ +
+ +

Test Criteria

+ +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/aws/raw HTTP/1.1
+Host: eph-ns.mcn-lab.f5demos.com
+                
+
+ +

+{
+    "info": {
+        "path": "/raw"
+    }
+    ...
+}   
+
+
+ + +
+ +
+
+ + +

+ Questions on this functionality are often asked on F5 DevCentral. Here's a hint. +

+ +
+

+ + +

+
+
+ temp +
+
+ temp +
+
+
+ +
+ +

Exercise 2: Request Header Shenanigans

+ +

+ While blind header insertion or deletion is useful in some use cases, this exercise focuses on context aware header manipulation. + Use the XC Header Processing docs for reference. +

+ +
+ +
+
    +
  • + + Insert a request header named X-MCN-src-site to identify the UDF CE to the origin. Do not use a static value. +
  • +
  • + + Insert a request header named X-MCN-namespace to identify the ephemeral NS to the origin. Do not use a static value. +
  • +
+
+ +
+ +

Test Criteria

+ +

+GET https://{{ ns or 'eph-ns '}}.mcn-lab.f5demos.com/ HTTP/1.1
+Host: {{ ns or 'eph-ns '}}.mcn-lab.f5demos.com
+                
+
+ +

+{
+    ...
+    "request_headers": {
+        "x-mcn-namespace": "wiggly-yellowtail",
+        "x-mcn-src-site": "{{ site or 'cluster-xxxxxxxxx' }}",
+    },
+    ...
+}
+            
+
+ + +
+ +
+
+ + +
+ +

Exercise 3: Response Header Shenanigans

+ +
+ +
+
    +
  • + + Insert a response header named X-MCN-dest-site to determine which cloud CE processed the request. +
  • +
+
+ +
+ +

Test Criteria

+ + + + + +Request 1 +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/aws HTTP/1.1
+Host: {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com
+                
+
+ +

+{
+    "x-mcn-dest-site": "student-awsnet"
+}
+            
+
+ +Request 2 +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/azure HTTP/1.1
+Host: {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com
+                    
+
+ +

+{
+    "x-mcn-dest-site": "student-azurenet"
+}
+                
+
+ +
+ +
+
+ + + + + + + + +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/overview.html b/labapp/app/templates/overview.html new file mode 100644 index 0000000..0230290 --- /dev/null +++ b/labapp/app/templates/overview.html @@ -0,0 +1,228 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +

+The lab environment, the service endpoints, and how you interact with the load balancer have been simplified in an effort to focus on concepts. +Understanding the environment, it's topology, and the rudimentary functionality of the cloud app will help in completing the exercises. +

+
+ +

Architecture

+ +

+The lab environment contains three distributed sites meshed using the F5 Distributed Cloud Global Network. +

+ +
    +
  • +     + student-awsnet in Amazon Web Services +
  • +
  • +     + student-azurenet in Microsoft Azure +
  • +
  • +     + Lab CE in UDF +
  • +
+ +
+ +
+ Arch +
+ +

Lab Exercises

+ +

+Lab exercises will ask you to create configuration in the lab tenant. +Exercise reqirements are listed in a table along with an object type indicator. +

+ +
    +
  • + + Load Balancer +
  • +
  • + + Origin Pool +
  • +
  • + + Route +
  • +
+ +
+ +

Test Criteria

+ +

+To complete lab exercises, you will run tests against the load balancer advertised from the Customer Edge in your UDF site. +You will build this load balancer in the first exercise. +All tests will be run from this web apllication. +

+

+Each test will specify success criteria followed by a button. +

+

+Here are some illustrative examples. +

+ +

+The first section of the test criteria shows the request being made. +

+ +

+GET https://foo.mcn-lab.f5demos.com/ HTTP/1.1
+Host: foo.mcn-lab.f5demos.com
+
+
+ +

+The second section shows a value the test expects in the response. +

+ +

+{
+  "info": {
+    "foo": True
+  }
+}
+
+
+ +
+ +
+
+ + +

+ The test made a request to https://foo.mcn-lab.f5demos.com. + The test succeeded because the response contained the JSON string {"info": { "foo": True }}. +

+ +
+ +

+GET https://foo.mcn-lab.f5demos.com/ HTTP/1.1
+Host: foo.mcn-lab.f5demos.com
+
+
+ +

+{
+  "info": {
+    "bar": True
+  }
+}
+
+
+ +
+ +
+
+ + +

+The test made a request to https://foo.mcn-lab.f5demos.com. +The test failed because the response did not contain the JSON string { "info": { "bar": True}}. +

+ +
+ +

Other Tools

+ +

curl and jq are provided on the UDF Runner instance.

+ +

+  ubuntu@ubuntu:~$ curl -s https://foo.mcn-lab.f5demos.com/ | jq
+  {
+    "info": {
+      "foo": true
+    }
+  }
+  
+ + + +
+ +

Cloud App

+ +

+Exercises are run against instances of the cloud app hosted in each remote cloud environment. +The cloud app simply echoes back HTTP request info. +Unless otherwise noted, test results display headers and info from the request received by the app. +In other words, test critera are evaluating being sent (as echoed back from the cloud app). + +To demonstrate, you can access an endpoint of each cloud app from your browser. +

+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+ +

Issues

+ +

+Use the lab repository issue tracker to report bugs, typos, or lab enhancements. +

+ + + +
+{% endblock %} + + + + + + + + + diff --git a/labapp/app/templates/portability.html b/labapp/app/templates/portability.html new file mode 100644 index 0000000..6aff2d2 --- /dev/null +++ b/labapp/app/templates/portability.html @@ -0,0 +1,163 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +
+ {% if not ns %} + + {% endif %} +
+ +

+ The configuration built so far handles load balancing, routing, and content manipulation. + XC refers to this object as a "load balancer" but it's really the holistic representation of an application whose service endpoints live across the distributed network. + The object is simple - it doesn't yet include configuration for WAAP, API protection, or a service policy. +

+

+ A key advantage of XC over traditional ADCs is its flexibility in specifying where a load balancer is advertised. +

+ +
+ +

Exercise 1: Advertise Policy

+ +
+
    +
  • + + Configure the load balancer to be advertised from the virtual site shared/mcn-practical-udf-sites. +
  • +
+
+ +
+ +

Test Criteria

+ +

+GET https://{{ ns or 'eph-ns '}}.mcn-lab.f5demos.com/ HTTP/1.1
+Host: {{ ns or 'eph-ns '}}.mcn-lab.f5demos.com
+                    
+
+ +

+{
+    "info": {
+        "path": "/"
+    }
+    ...
+}
+                
+
+ +
+ +
+
+ + +
+

+ +

+
+
+ temp +
+ temp +
+
+
+ +
+ +

Exercise 2: Find a Friend

+ +

+ Do you have a friend working on the lab? + Have they updated their advertise policy to use the virtual site? + Find their ephemeral namespace (or use the one provided in the form). +

+ +
+
    +
  • + + Test if your friend's load balancer is being advertised to the UDF site. +
  • +
+
+ +
+ +

Test Criteria

+ + + +

+GET https://wiggly-yellowtail.mcn-lab.f5demos.com/ HTTP/1.1
+Host: wiggly-yellowtail.mcn-lab.f5demos.com
+                        
+
+ +

+{
+    "info": {
+        "path": "/"
+    }
+    ...
+}
+                    
+
+ +
+
+
+
+ + +
+
+
+
+
+ + + + + + + + + + +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/route.html b/labapp/app/templates/route.html new file mode 100644 index 0000000..1203667 --- /dev/null +++ b/labapp/app/templates/route.html @@ -0,0 +1,189 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + +
+ {% if not ns %} + + {% endif %} +
+ +

+Modern applications, and some classic ones, are often comprised of disparate services spread across sites. +MCN solutions must be able to make routing decisions based on characterstics of an HTTP request. +F5 XC App Connect is a distributed L7 proxy that provides intelligent routing, visibility, and strategic points of control. +

+ +
+ +

Exercise 1: Path Routing

+ +

+Build routing rules and configure your load balancer to route traffic between the two cloud apps based on the request url. +

+ +
+
    +
  • + + Reuse the origin pools from the previous exercise. +
  • +
  • + + Route requests to https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/aws to the AWS cloud app. +
  • +
  • + + Route requests to https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/azure to the Azure cloud app. +
  • +
+
+ + +
+ +

Test Criteria

+ + + +Request 1 +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/aws/raw HTTP/1.1
+Host: {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com
+    
+
+ +

+{
+    "env": "aws",
+    ...
+}
+
+
+ +Request 2 +

+GET https://{{ ns or 'eph-ns' }}.mcn-lab.f5demos.com/azure/raw HTTP/1.1
+Host: {{ ns or 'eph-ns' }}.mcn-lab.f5demos.com
+        
+
+ +

+{
+    "env": "azure",
+    ...
+}
+        
+
+ +
+ +
+ +
+
+ + + + +

Exercise 2: Header Routing

+ +

+Build rules to route traffic between the two cloud apps based on an arbitrary HTTP request header. +

+ +
+
    +
  • + + Route requests with an X-MCN-Lab: aws header to the AWS cloud app. +
  • +
  • + + Route requests with an X-MCN-Lab: azure header to the Azure cloud app. +
  • +
+
+ +
+ +

Test Criteria

+ + + +Request 1 +

+GET https://{{ ns or 'eph-ns '}}.mcn-lab.f5demos.com/raw HTTP/1.1
+Host: {{ ns or 'eph-ns '}}.mcn-lab.f5demos.com
+X-MCN-lab: aws
+            
+
+ +

+{
+    "env": "aws",
+    ...
+}
+        
+
+ +Request 2 +

+GET https://{{ ns or 'eph-ns '}}.mcn-lab.f5demos.com/raw HTTP/1.1
+Host: {{ ns or 'eph-ns '}}.mcn-lab.f5demos.com
+X-MCN-lab: azure
+                
+
+ +

+{
+    "env": "azure",
+    ...
+}
+            
+
+ +
+ +
+
+ + + + + + + + + +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/score.html b/labapp/app/templates/score.html new file mode 100644 index 0000000..5314b15 --- /dev/null +++ b/labapp/app/templates/score.html @@ -0,0 +1,85 @@ + +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} + + +
+ + +
+ Banner +
+ + + + +
+
+
+
+ {{ lb_table|safe }} +
+
+
+
+ {{ route_table|safe }} +
+
+
+
+
+
+ {{ manip_table|safe }} +
+
+
+
+ {{ port_table|safe }} +
+
+
+
+ +
+ +
+ + + +
+ +
+

Feedback

+

+ + Please tell us your thoughts about the lab using this form. +

+
+

Issues

+

+ Use the lab repository issue tracker to report bugs, typos, or lab enhancements. +

+
+{% endblock %} + diff --git a/labapp/app/templates/setup.html b/labapp/app/templates/setup.html index b97651d..2a992ef 100644 --- a/labapp/app/templates/setup.html +++ b/labapp/app/templates/setup.html @@ -3,10 +3,50 @@ {% block title %}{{ title }}{% endblock %} {% block content %} -
- {{ content|safe }} + -
+
+ +
+ Banner +
+ + + +

+ Log in to the lab tenant and open any namespaced tile - Multi-Cloud App Connect, Distributed Apps, etc. + The ephemeral namespace is a randomly generated concatenation of adjective-animal in the navigation bar towards the top. +

+ + eph-ns + +

+ The ephemeral namespace will be used to derive a unique URL for the load balancer used in the lab exercises. +

+ +
+
+ + +
+ + +
+ + +
{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %}
@@ -23,7 +63,7 @@ diff --git a/labapp/app/templates/standard.html b/labapp/app/templates/standard.html index 1b2d796..72b588a 100644 --- a/labapp/app/templates/standard.html +++ b/labapp/app/templates/standard.html @@ -3,7 +3,7 @@ {% block title %}{{ title }}{% endblock %} {% block content %} -
+
{{ content|safe }}
{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/welcome.html b/labapp/app/templates/welcome.html new file mode 100644 index 0000000..4d72a61 --- /dev/null +++ b/labapp/app/templates/welcome.html @@ -0,0 +1,103 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ +
+ Banner +
+ + + + + +
+ +

Getting Started

+ +

+ When your UDF deployment launched, two automated processes started - Customer Edge ("CE") registration and account provisioning in the lab tenant. +

+ +
+ +

Customer Edge

+ +

+ The CE in the UDF deployment will register with the lab tenant. + CEs on first launch update software and, often, their OS. This can take ~20 min from when the CE is booted. +

+ +

+ When the CE is ready, the status indicator in the navigation pane (👀 look to the left) will show . + Use the indicator to find the CE site name needed for configuring the load balancer's advertise policy. +

+
+ +

Account Provisioning

+ +

+ Check the email used to launch your UDF deployment for a welcome or password reset email from the lab tenant. + Update your password to log into the tenant. +

+ +
+
+
+ + + +
+
+ + + +
+
+
+ +
+ +

While You Wait

+ +
+ Here's a few things you can do while waiting for the CE to be registered and provisioned: +
+
    +
  • +     + Read the lab overview. +
  • +
  • +     + Check for the tenant welcome email. +
  • +
  • +     + Configure lab setup after logging into the tenant. +
  • +
  • +     + Get a cup of coffee. +
  • + +
+
+ + + + + + +
+{% endblock %} \ No newline at end of file