diff --git a/cloudapp/app/templates/pretty_echo.html b/cloudapp/app/templates/pretty_echo.html index 3360474..0ad7412 100644 --- a/cloudapp/app/templates/pretty_echo.html +++ b/cloudapp/app/templates/pretty_echo.html @@ -33,18 +33,18 @@

- + MCN Practical Cloud App

Environment
{% if request_env == 'AWS' %} - + {% elif request_env == 'Azure' %} - + {% else %} - + {% endif %}  {{ request_env }}
diff --git a/labapp/app/app.py b/labapp/app/app.py index 6d7d395..2146682 100644 --- a/labapp/app/app.py +++ b/labapp/app/app.py @@ -1,12 +1,12 @@ """ Flask app for lab/guide """ +import os +import re from flask import Flask, render_template, jsonify, request, redirect, make_response, flash, url_for from flask_caching import Cache import requests import markdown -import validators -import os from ce import get_ce_info, get_ce_state app = Flask(__name__) @@ -15,11 +15,31 @@ if app.config['udf']: info = get_ce_info() app.config['ce_info'] = info -app.config['base_url'] = "lab-mcn.f5demos.com" +app.config['base_url'] = "mcn-lab.f5demos.com" app.config['CACHE_TYPE'] = 'SimpleCache' cache = Cache(app) app.secret_key = "blahblahblah" +def render_md(file: str) -> str: + """render markdown w/ common extentions""" + with open(file, "r") as file: + content = file.read() + html = markdown.markdown( + content, + extensions=['markdown.extensions.attr_list','markdown.extensions.codehilite','markdown.extensions.fenced_code'] + ) + return html + +def validate_eph_ns(input_name): + """validate ephemeral namespace name""" + pattern = r'^[a-zA-Z]+-[a-zA-Z]+$' + return bool(re.match(pattern, input_name)) + +def eph_ns() -> str: + """check if ephemeral namespace is set""" + eph_ns = request.cookies.get('eph_ns', None) + return eph_ns + @app.errorhandler(404) @app.errorhandler(500) def return_err(err): @@ -32,92 +52,89 @@ def return_err(err): @app.route('/') def index(): - with open("markdown/overview.md", "r") as file: - content = file.read() - html = markdown.markdown(content) + """index page""" + html = render_md("markdown/overview.md") return render_template('overview.html', content=html) @app.route('/setup', methods=['GET', 'POST']) def setup(): + """setup page""" if request.method == 'POST': action = request.form['action'] if action == 'save': - base_url = request.form['base_url'].strip() - if not validators.domain(base_url): - flash("Invalid domain format.", "info") - return redirect(url_for('setup')) - if not base_url.endswith(app.config['base_url']): - flash(f"Domain must end with {app.config['base_url']}.", "info") + eph_ns = request.form['eph_ns'].strip() + print(eph_ns) + if not validate_eph_ns(eph_ns): + flash("Invalid ephemeral NS.", "danger") return redirect(url_for('setup')) response = make_response(redirect('/setup')) - response.set_cookie('base_url', base_url, max_age=60*60*24) - flash("Domain successfully set.", "success") + response.set_cookie('eph_ns', eph_ns, max_age=60*60*24) + flash("Ephemeral NS successfully set.", "success") return response elif action == 'clear': response = make_response(redirect('/setup')) - response.set_cookie('base_url', '', expires=0) - flash("Domain setting cleared.", "info") + response.set_cookie('eph_ns', '', expires=0) + flash("Ephemeral NS cleared.", "info") return response - return render_template('setup.html', base_url=app.config['base_url']) + html = render_md("markdown/setup.md") + return render_template('setup.html', content=html) + +@app.route('/arch') +def arch(): + """arch page""" + html = render_md("markdown/arch.md") + return render_template('standard.html', content=html, title="MCN Practical: Architecture") @app.route('/_ce_state') @cache.cached(timeout=30) def ce_state(): + """get ce state (internal route)""" data = get_ce_state(app.config['ce_info']) return data -@app.route('/test') -def test(): - base_url = request.cookies.get('base_url') - url = f"https://echo.{base_url}" - try: - response = requests.get(url) - response.raise_for_status() - return jsonify(status='success', data=response.json()) - except requests.RequestException as e: - return jsonify(status='fail', error=str(e)) - @app.route('/lb') def lb(): - with open("markdown/lb.md", "r") as file: - content = file.read() - html = markdown.markdown( - content, - extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code'] - ) - return render_template('lb.html', content=html) + """lb page""" + ns = eph_ns() + html = render_md("markdown/lb.md") + return render_template('exercise_standard.html', title="MCN Practical: LB", content=html, ns=ns) @app.route('/path') def path(): - with open("markdown/path.md", "r") as file: - content = file.read() - html = markdown.markdown( - content, - extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code'] - ) - return render_template('path.html', content=html) + """path page""" + ns = eph_ns() + html = render_md("markdown/path.md") + return render_template('exercise_standard.html', title="MCN Practical: Path Routing", content=html, ns=ns) @app.route('/header') def header(): - with open("markdown/header.md", "r") as file: - content = file.read() - html = markdown.markdown( - content, - extensions=['markdown.extensions.codehilite','markdown.extensions.fenced_code'] - ) - return render_template('header.html', context=html) + """header page""" + ns = eph_ns() + html = render_md("markdown/header.md") + return render_template('exercise_standard.html', title="MCN Practical: Headers", content=html, ns=ns) -@app.route('/appCon-aws') -def make_request_ac1_aws(): +@app.route('/_lb_aws') +def lb_aws(): + """AWS LB test""" try: - response = requests.get('https://ifconfig.io/all.json') - response.raise_for_status() + ns = eph_ns() + if not ns: + raise Exception("Ephemeral NS not set.") + url = f"https://{ns}.{app.config['base_url']}/raw" + print(url) + response = requests.get(url, timeout=5) + print(response.text) + print(response.json()) + response.raise_for_status() + if response.json()['request_env'] != "AWS": + raise Exception("Invalid request env.") return jsonify(status='success', data=response.json()) - except requests.RequestException as e: + except Exception as e: return jsonify(status='fail', error=str(e)) -@app.route('/appCon-azure') -def make_request_ac1_azure(): +@app.route('/_lb_azure') +def lb_azure(): + """Azure LB test""" try: response = requests.get('https://ifconfig1.io/all.json') response.raise_for_status() diff --git a/labapp/app/markdown/arch.md b/labapp/app/markdown/arch.md new file mode 100644 index 0000000..52f46a9 --- /dev/null +++ b/labapp/app/markdown/arch.md @@ -0,0 +1,14 @@ + + +# **Architecture** + + + + + diff --git a/labapp/app/markdown/header.md b/labapp/app/markdown/header.md new file mode 100644 index 0000000..e795016 --- /dev/null +++ b/labapp/app/markdown/header.md @@ -0,0 +1,44 @@ + + +# **Header Manipulation** + + + +
+ +### **Exercise 1: Add/Remove** + +HERE + +
+ +
+
+ + +
+ +Nice 🚀! If you've completed all the exercises so far, you have a good foundation for how App Connect addresses common L7 MCN scenarios. +In subsequent labs, we'll explore security and observabilty concepts that build on MCN functionality. +Head over to the Network Connect exercise. + +
\ No newline at end of file diff --git a/labapp/app/markdown/home-overview.md b/labapp/app/markdown/home-overview.md deleted file mode 100644 index a247dfb..0000000 --- a/labapp/app/markdown/home-overview.md +++ /dev/null @@ -1,5 +0,0 @@ -# **Overview** - -This lab is a "practical" training activity. -Each exercise will ask you to **configure** F5 Distributed Cloud (XC) objects to reinforce a core XC Multi-Cloud networking concept. -Once configured, you'll be asked to **test** your configuration from this web application. \ No newline at end of file diff --git a/labapp/app/markdown/lb.md b/labapp/app/markdown/lb.md index 072bad7..b1d34f3 100644 --- a/labapp/app/markdown/lb.md +++ b/labapp/app/markdown/lb.md @@ -1,30 +1,170 @@ -# Load Balancer + -This is a sample page to demonstrate Markdown integration. +# **Load Balancing** -## Features + -- **Easy to write**: Markdown is straightforward. -- **Flexible**: Easily convert to HTML. -- **Portable**: Use Markdown files across different platforms. +Load balancing is the cornerstone of XC's App Connect functionality. +L7 MCN requires discovering services at one site and making those services available at some other site. +That's accomplished by configuring origin pools and load balancers. +More complicated configurations (underlay networking, security services, observability, etc.) are built on these primitives. -## Code Test +
-``` -POST /task?id=1 HTTP/1.1 -Host: example.org -Content-Type: application/json; charset=utf-8 -Content-Length: 137 +### **Exercise 1: AWS Cloud App** -{ - "status": "ok", - "extended": true, - "results": [ - {"value": 0, "type": "int64"}, - {"value": 1.0e+3, "type": "decimal"} - ] -} -``` +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 following criteria: -Enjoy using Markdown! + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ +
+ +
+
+ + +
+ +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. + +
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+
+ + +
+ +Once you've completed both exercises, move on to the path based routing exercise. + +
diff --git a/labapp/app/markdown/overview.md b/labapp/app/markdown/overview.md index b057a0f..c518123 100644 --- a/labapp/app/markdown/overview.md +++ b/labapp/app/markdown/overview.md @@ -1,32 +1,53 @@ -
- -
- -# **Overview** +
+ 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") concepts. -Once configured, you'll be asked to **test** your configuration from this web application. +Once configured, you'll be asked to **test** your 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](https://f5-xc-lab-mcn.console.ves.volterra.io/). + +
+ +## **Customer Edge** -
+The CE in your UDF deployment is being registered with the [lab tenant](https://f5-xc-lab-mcn.console.ves.volterra.io/). +CEs on first launch update software and, often, thier OS. This can be very time consuming. +This process will take 15-20 min from when the CE is booted. +You can still get started on some preliminary tasks while waiting. -# **Setup** + -When your UDF deployment launched, two automated processes started. +**Note on status in nav and status page.** + +
+ +## **Account Provisioning** + +Check the email used to launch your UDF deployment for a "welcome" or password reset email to the [lab tenant](https://f5-xc-lab-mcn.console.ves.volterra.io/). +Update your password and log into the tenant.

- - + +

-Check the email used to launch your UDF deployment for a "welcome" or password reset email to the [F5-XC-MCN-Lab](https://f5-xc-lab-mcn.console.ves.volterra.io/) tenant. + + +
+ -
- -The Customer Edge in your UDF deployment is being registered with the [F5-XC-MCN-Lab](https://f5-xc-lab-mcn.console.ves.volterra.io/) tenant. -CEs on first launch update software and, often, thier OS. This can be very time consuming. diff --git a/labapp/app/markdown/path.md b/labapp/app/markdown/path.md new file mode 100644 index 0000000..00fc69f --- /dev/null +++ b/labapp/app/markdown/path.md @@ -0,0 +1,42 @@ + + +# **Path Based Routing** + + + +
+ +### **Exercise 1: Origin Routing** + +HERE + +
+ +
+
+ + +
+ +Once you've completed both exercises, move on to the header manipulation exercise. + +
\ No newline at end of file diff --git a/labapp/app/markdown/setup.md b/labapp/app/markdown/setup.md new file mode 100644 index 0000000..364a159 --- /dev/null +++ b/labapp/app/markdown/setup.md @@ -0,0 +1,35 @@ + + +# **Setup** + + + +Log in to the [lab tenant](https://f5-xc-lab-mcn.console.ves.volterra.io/) and open any namespaced tile (Multi-Cloud App Connect, Distributed Apps, etc.). Your ephemeral NS name is a randomly generated concatenation of _adjective_-_animal_. + +eph-ns + +The ephemeral NS name will be used to derive a unique URL for the load balancer used in these exercises. + +
+
+ + +
+ + +
+ \ No newline at end of file diff --git a/labapp/app/static/arch.png b/labapp/app/static/arch.png new file mode 100644 index 0000000..8c8e283 Binary files /dev/null and b/labapp/app/static/arch.png differ diff --git a/labapp/app/static/custom.css b/labapp/app/static/custom.css index ec45ad2..96a865b 100644 --- a/labapp/app/static/custom.css +++ b/labapp/app/static/custom.css @@ -19,6 +19,10 @@ main { overflow-y: auto; } +.markdown-body { + padding: 50px; +} + .err { display: flex; justify-content: center; /* Centers horizontally */ diff --git a/labapp/app/static/eph-ns.png b/labapp/app/static/eph-ns.png new file mode 100644 index 0000000..f4379cc Binary files /dev/null and b/labapp/app/static/eph-ns.png differ diff --git a/labapp/app/static/load-balancer1.png b/labapp/app/static/load-balancer1.png new file mode 100644 index 0000000..631f669 Binary files /dev/null and b/labapp/app/static/load-balancer1.png differ diff --git a/labapp/app/static/load-balancer2.png b/labapp/app/static/load-balancer2.png new file mode 100644 index 0000000..7968766 Binary files /dev/null and b/labapp/app/static/load-balancer2.png differ diff --git a/labapp/app/static/origin-pool.png b/labapp/app/static/origin-pool.png new file mode 100644 index 0000000..2a02aa9 Binary files /dev/null and b/labapp/app/static/origin-pool.png differ diff --git a/labapp/app/static/origin-server.png b/labapp/app/static/origin-server.png new file mode 100644 index 0000000..49b412c Binary files /dev/null and b/labapp/app/static/origin-server.png differ diff --git a/labapp/app/static/path-routing.png b/labapp/app/static/path.png similarity index 100% rename from labapp/app/static/path-routing.png rename to labapp/app/static/path.png diff --git a/labapp/app/static/setup.png b/labapp/app/static/setup.png new file mode 100644 index 0000000..87576c5 Binary files /dev/null and b/labapp/app/static/setup.png differ diff --git a/labapp/app/templates/base.html b/labapp/app/templates/base.html index e5dd359..c601e0b 100644 --- a/labapp/app/templates/base.html +++ b/labapp/app/templates/base.html @@ -88,7 +88,8 @@ diff --git a/labapp/app/templates/coming-soon.html b/labapp/app/templates/coming-soon.html new file mode 100644 index 0000000..28fc6e8 --- /dev/null +++ b/labapp/app/templates/coming-soon.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block title %}MCN Practical Error{% endblock %} + +{% block content %} +
+ + Descriptive Text + +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/exercise_standard.html b/labapp/app/templates/exercise_standard.html new file mode 100644 index 0000000..b2b0b0e --- /dev/null +++ b/labapp/app/templates/exercise_standard.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} + +
+ {% if not ns %} + + {% endif %} +
+ +
+ {{ content|safe }} +
+{% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/index.html b/labapp/app/templates/index.html.delete similarity index 100% rename from labapp/app/templates/index.html rename to labapp/app/templates/index.html.delete diff --git a/labapp/app/templates/lb.html b/labapp/app/templates/lb.html index 08374d8..9a65b1b 100644 --- a/labapp/app/templates/lb.html +++ b/labapp/app/templates/lb.html @@ -2,57 +2,18 @@ {% block title %}MCN Practical: Load Balancer{% endblock %} +{% with messages = get_flashed_messages(with_categories=true) %} +{% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+{% endif %} +{% endwith %} + {% block content %}
{{ content|safe }}
- - -

Test AWS Load Balancer

-
- -
-
- - -

Test Azure Load Balancer

-
- -
-
- {% endblock %} \ No newline at end of file diff --git a/labapp/app/templates/setup.html b/labapp/app/templates/setup.html index 70a3a33..6d79535 100644 --- a/labapp/app/templates/setup.html +++ b/labapp/app/templates/setup.html @@ -1,38 +1,22 @@ {% extends "base.html" %} -{% block content %} -

Base Domain Setup

-
-
- - -
- - -
+{% block title %}MCN Practical: Setup{% endblock %} - +{% block content %} +
+ {{ content|safe }} -{% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} -
- {% for category, message in messages %} -
{{ message }}
- {% endfor %} -
- {% endif %} -{% endwith %} + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+ {% endif %} + {% endwith %} + +
{% endblock %} + diff --git a/labapp/app/templates/setup.html.delete b/labapp/app/templates/setup.html.delete new file mode 100644 index 0000000..70a3a33 --- /dev/null +++ b/labapp/app/templates/setup.html.delete @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block content %} +

Base Domain Setup

+
+
+ + +
+ + +
+ + + +{% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+ {% endif %} +{% endwith %} +{% endblock %} + diff --git a/labapp/app/templates/standard.html b/labapp/app/templates/standard.html new file mode 100644 index 0000000..1b2d796 --- /dev/null +++ b/labapp/app/templates/standard.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block title %}{{ title }}{% endblock %} + +{% block content %} +
+ {{ content|safe }} +
+{% endblock %} \ No newline at end of file