Skip to content
Snippets Groups Projects
Commit 1e0f29d2 authored by Karen Pailozian's avatar Karen Pailozian
Browse files

Add sample notebook for starting VM on Openstack from jupyter hub

parent 186e89ce
No related branches found
No related tags found
1 merge request!1Sample notebook for EOSC
figures/network_topology.png

59.9 KiB

figures/network_topology_vm.png

60.3 KiB

figures/scheme.png

43.5 KiB

%% Cell type:markdown id:f1d44b71-9709-467e-92d4-c1faa31a93e7 tags:
# Deploying Your First Virtual Machine on PSNC OpenStack
Welcome to this hands-on tutorial for launching and configuring your first Virtual Machine (VM) in the PSNC OpenStack cloud environment. This guide will walk you through each step of the deployment process—from authentication to full VM provisioning and access.
📚 **Further reading:** [PSNC OpenStack Virtual Machines](https://docs.psnc.pl/display/EOSCUserGuides/Virtual+Machines)
%% Cell type:markdown id:00e621e9-5493-45aa-8cea-dcbb14ab0df6 tags:
# Install the required libraries
%% Cell type:code id:0ce0f97a-a87b-4655-8f85-cfabb07172f7 tags:
``` python
pip install python-openstackclient python-keystoneclient ipywidgets paramiko
```
%% Output
Requirement already satisfied: python-openstackclient in /opt/conda/lib/python3.12/site-packages (8.0.0)
Requirement already satisfied: python-keystoneclient in /opt/conda/lib/python3.12/site-packages (5.6.0)
Requirement already satisfied: ipywidgets in /opt/conda/lib/python3.12/site-packages (8.1.5)
Requirement already satisfied: paramiko in /opt/conda/lib/python3.12/site-packages (3.5.1)
Requirement already satisfied: pbr!=2.1.0,>=2.0.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (6.1.1)
Requirement already satisfied: cryptography>=2.7 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (44.0.0)
Requirement already satisfied: cliff>=3.5.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (4.9.1)
Requirement already satisfied: iso8601>=0.1.11 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (2.1.0)
Requirement already satisfied: openstacksdk>=3.3.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (4.5.0)
Requirement already satisfied: osc-lib>=2.3.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (4.0.0)
Requirement already satisfied: oslo.i18n>=3.15.3 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (6.5.1)
Requirement already satisfied: python-cinderclient>=3.3.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (9.7.0)
Requirement already satisfied: requests>=2.27.0 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (2.32.3)
Requirement already satisfied: stevedore>=2.0.1 in /opt/conda/lib/python3.12/site-packages (from python-openstackclient) (5.4.1)
Requirement already satisfied: debtcollector>=1.2.0 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (3.0.0)
Requirement already satisfied: keystoneauth1>=3.4.0 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (5.10.0)
Requirement already satisfied: oslo.config>=5.2.0 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (9.7.1)
Requirement already satisfied: oslo.serialization>=2.18.0 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (5.7.0)
Requirement already satisfied: oslo.utils>=3.33.0 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (8.2.0)
Requirement already satisfied: packaging>=20.4 in /opt/conda/lib/python3.12/site-packages (from python-keystoneclient) (24.2)
Requirement already satisfied: comm>=0.1.3 in /opt/conda/lib/python3.12/site-packages (from ipywidgets) (0.2.2)
Requirement already satisfied: ipython>=6.1.0 in /opt/conda/lib/python3.12/site-packages (from ipywidgets) (8.31.0)
Requirement already satisfied: traitlets>=4.3.1 in /opt/conda/lib/python3.12/site-packages (from ipywidgets) (5.14.3)
Requirement already satisfied: widgetsnbextension~=4.0.12 in /opt/conda/lib/python3.12/site-packages (from ipywidgets) (4.0.13)
Requirement already satisfied: jupyterlab_widgets~=3.0.12 in /opt/conda/lib/python3.12/site-packages (from ipywidgets) (3.0.13)
Requirement already satisfied: bcrypt>=3.2 in /opt/conda/lib/python3.12/site-packages (from paramiko) (4.3.0)
Requirement already satisfied: pynacl>=1.5 in /opt/conda/lib/python3.12/site-packages (from paramiko) (1.5.0)
Requirement already satisfied: autopage>=0.4.0 in /opt/conda/lib/python3.12/site-packages (from cliff>=3.5.0->python-openstackclient) (0.5.2)
Requirement already satisfied: cmd2>=1.0.0 in /opt/conda/lib/python3.12/site-packages (from cliff>=3.5.0->python-openstackclient) (2.5.11)
Requirement already satisfied: PrettyTable>=0.7.2 in /opt/conda/lib/python3.12/site-packages (from cliff>=3.5.0->python-openstackclient) (3.16.0)
Requirement already satisfied: PyYAML>=3.12 in /opt/conda/lib/python3.12/site-packages (from cliff>=3.5.0->python-openstackclient) (6.0.2)
Requirement already satisfied: cffi>=1.12 in /opt/conda/lib/python3.12/site-packages (from cryptography>=2.7->python-openstackclient) (1.17.1)
Requirement already satisfied: wrapt>=1.7.0 in /opt/conda/lib/python3.12/site-packages (from debtcollector>=1.2.0->python-keystoneclient) (1.17.2)
Requirement already satisfied: decorator in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (5.1.1)
Requirement already satisfied: jedi>=0.16 in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.2)
Requirement already satisfied: matplotlib-inline in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.7)
Requirement already satisfied: pexpect>4.3 in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (4.9.0)
Requirement already satisfied: prompt_toolkit<3.1.0,>=3.0.41 in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (3.0.48)
Requirement already satisfied: pygments>=2.4.0 in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (2.18.0)
Requirement already satisfied: stack_data in /opt/conda/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)
Requirement already satisfied: os-service-types>=1.2.0 in /opt/conda/lib/python3.12/site-packages (from keystoneauth1>=3.4.0->python-keystoneclient) (1.7.0)
Requirement already satisfied: typing-extensions>=4.12 in /opt/conda/lib/python3.12/site-packages (from keystoneauth1>=3.4.0->python-keystoneclient) (4.12.2)
Requirement already satisfied: dogpile.cache>=0.6.5 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (1.3.4)
Requirement already satisfied: jmespath>=0.9.0 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (1.0.1)
Requirement already satisfied: jsonpatch!=1.20,>=1.16 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (1.33)
Requirement already satisfied: platformdirs>=3 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (4.3.6)
Requirement already satisfied: psutil>=3.2.2 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (5.9.8)
Requirement already satisfied: requestsexceptions>=1.2.0 in /opt/conda/lib/python3.12/site-packages (from openstacksdk>=3.3.0->python-openstackclient) (1.4.0)
Requirement already satisfied: netaddr>=0.7.18 in /opt/conda/lib/python3.12/site-packages (from oslo.config>=5.2.0->python-keystoneclient) (1.3.0)
Requirement already satisfied: rfc3986>=1.2.0 in /opt/conda/lib/python3.12/site-packages (from oslo.config>=5.2.0->python-keystoneclient) (2.0.0)
Requirement already satisfied: msgpack>=0.5.2 in /opt/conda/lib/python3.12/site-packages (from oslo.serialization>=2.18.0->python-keystoneclient) (1.1.0)
Requirement already satisfied: tzdata>=2022.4 in /opt/conda/lib/python3.12/site-packages (from oslo.serialization>=2.18.0->python-keystoneclient) (2024.2)
Requirement already satisfied: pyparsing>=2.1.0 in /opt/conda/lib/python3.12/site-packages (from oslo.utils>=3.33.0->python-keystoneclient) (3.2.0)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.12/site-packages (from pbr!=2.1.0,>=2.0.0->python-openstackclient) (75.6.0)
Requirement already satisfied: charset_normalizer<4,>=2 in /opt/conda/lib/python3.12/site-packages (from requests>=2.27.0->python-openstackclient) (3.4.0)
Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.12/site-packages (from requests>=2.27.0->python-openstackclient) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/conda/lib/python3.12/site-packages (from requests>=2.27.0->python-openstackclient) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.12/site-packages (from requests>=2.27.0->python-openstackclient) (2024.12.14)
Requirement already satisfied: pycparser in /opt/conda/lib/python3.12/site-packages (from cffi>=1.12->cryptography>=2.7->python-openstackclient) (2.22)
Requirement already satisfied: pyperclip>=1.8 in /opt/conda/lib/python3.12/site-packages (from cmd2>=1.0.0->cliff>=3.5.0->python-openstackclient) (1.9.0)
Requirement already satisfied: wcwidth>=0.2.10 in /opt/conda/lib/python3.12/site-packages (from cmd2>=1.0.0->cliff>=3.5.0->python-openstackclient) (0.2.13)
Requirement already satisfied: parso<0.9.0,>=0.8.4 in /opt/conda/lib/python3.12/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets) (0.8.4)
Requirement already satisfied: jsonpointer>=1.9 in /opt/conda/lib/python3.12/site-packages (from jsonpatch!=1.20,>=1.16->openstacksdk>=3.3.0->python-openstackclient) (3.0.0)
Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.12/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)
Requirement already satisfied: executing>=1.2.0 in /opt/conda/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (2.1.0)
Requirement already satisfied: asttokens>=2.1.0 in /opt/conda/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (3.0.0)
Requirement already satisfied: pure_eval in /opt/conda/lib/python3.12/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (0.2.3)
Note: you may need to restart the kernel to use updated packages.
%% Cell type:code id:c3718348-c7df-44d3-9811-66b7fa7ec7fb tags:
``` python
import sys
import paramiko
import yaml
from keystoneauth1.session import Session
from keystoneauth1.identity.v3.oidc import OidcAccessToken
from keystoneauth1.identity.v3.application_credential import ApplicationCredential
from keystoneclient.v3.client import Client
from openstack.connection import Connection
import ipywidgets as widgets
import base64
import time
from IPython.display import display, clear_output, Markdown
from openstack.exceptions import ResourceNotFound, SDKException
```
%% Cell type:markdown id:02aedafa-9276-4350-b60c-53fe216514c9 tags:
# 🔐 Connect to OpenStack
## Set up OpenStack session with OIDC token authentication
To authenticate with OpenStack using OIDC (OpenID Connect), you'll need to provide the following parameters:
- **`auth_url`**: The URL of the OpenStack authentication server.
- **`protocol`**: The protocol used for authentication, such as `openid`.
- **`identity_provider`**: The name of the trusted external Identity Provider (IdP) that authenticates your identity.
- **`access_token`**: The token you receive from the IdP after successfully logging in.
This token proves your identity to OpenStack.
> ⚙️ These parameters are used to initialize an authenticated session with OpenStack through the `openstack.connect()` function.
%% Cell type:code id:5bc6fac0-c5fa-44a7-9d29-38ea5164e4bc tags:
``` python
def load_access_token(token_file_path="/var/run/secrets/oidc/access_token"):
"""Reads access token from specified file"""
try:
return open(token_file_path, "r").read()
except IOError:
print("Reading from access token file failed.", file=sys.stderr)
cloud_creds = OidcAccessToken(auth_url="https://api.cloud.psnc.pl:5000/v3/",
identity_provider="aai.open-science-cloud.ec.europa.eu",
protocol="openid",
access_token=load_access_token()
)
cloud_session = Session(auth=cloud_creds)
```
%% Cell type:markdown id:c27a6b45-cd62-446c-bccf-9cba425e54ab tags:
## OpenStack Keystone Client
The Keystone client is used to interact with OpenStack's identity service and enables to:
- Access OpenStack components not exposed in the primary API.
- Retrieve detailed information about your projects
%% Cell type:code id:b871dc55-172a-43da-81d7-b7fa796a7c2c tags:
``` python
# Get list of accessible projects
keystone_client = Client(session=cloud_session)
my_projects = keystone_client.auth.projects()
```
%% Cell type:code id:9a58d8c5-7dcd-4549-9b10-0456ccd66b30 tags:
``` python
clear_output(wait=True) # Clear previous output first
display(Markdown("## Scope to one of your projects you are entitled to access"))
project_dropdown = widgets.Dropdown(
options=[(project.name, idx) for idx, project in enumerate(my_projects)],
description="Project:",
layout=widgets.Layout(width='50%')
)
connect_button = widgets.Button(description="Connect to project", button_style="success")
output = widgets.Output()
def on_connect_clicked(b):
with output:
clear_output()
idx = project_dropdown.value
selected_project = my_projects[idx]
print(f"🔌 Connecting to project: {selected_project.name} (ID: {selected_project.id}, Domain: {selected_project.domain_id})")
scoped_cloud_creds = OidcAccessToken(
auth_url="https://api.cloud.psnc.pl:5000/v3/",
identity_provider="aai.open-science-cloud.ec.europa.eu",
protocol="openid",
project_id=selected_project.id,
project_domain_id=selected_project.domain_id,
access_token=load_access_token()
)
global scoped_cloud_session, scoped_openstack_connection
scoped_cloud_session = Session(auth=scoped_cloud_creds)
scoped_openstack_connection = Connection(session=scoped_cloud_session)
print("✅ Connection established successfully!")
connect_button._click_handlers.callbacks.clear()
connect_button.on_click(on_connect_clicked)
display(project_dropdown, connect_button, output)
```
%% Output
## Scope to one of your projects you are entitled to access
%% Cell type:code id:7c50e961-b797-4e1c-a91b-e44487324c0c tags:
``` python
print("Your current project id: ", scoped_openstack_connection.current_project_id)
```
%% Output
Your current project id: db6b4eb2d6fe454191e8d39b088564cd
%% Cell type:markdown id:5a8c4205-e524-44ca-96fb-d7577bc74d90 tags:
# Starting up your own VM
## 🗝️ Create an SSH Key Pair for VM Access
- If you already have a key, skip this step and provide the key name in the `create_server()` method.
- You can:
- Upload your **own** public key using `public_key="ssh-rsa ..."`
- Or let **OpenStack generate one** for you (as we’ll do below).
- Remember to save the private key securely!
%% Cell type:code id:5e4b3a58-687b-471f-8c7f-a6db86bde820 tags:
``` python
key_name = "key"
new_keypair = scoped_openstack_connection.create_keypair(key_name)
#Generating key-pair using existing public key
#scoped_openstack_connection.create_keypair("mykey2", public_key="ssh-rsa ....")
#If you let OpenStack generate your key-pair you will need to save your
#private and public keys for later use
with open(f"{key_name}.pem", "w") as priv_file:
priv_file.write(new_keypair.private_key)
print(f"Private key saved to {key_name}.pem")
with open(f"{key_name}.pub", "w") as pub_file:
pub_file.write(new_keypair.public_key)
print(f"Public key saved to {key_name}.pub")
```
%% Output
Private key saved to key.pem
Public key saved to key.pub
%% Cell type:markdown id:809ce42b-b70f-4dfb-82a9-cb505477663c tags:
## Create a Security Group for Your VM
Before creating a virtual machine, it's important to define what kind of network traffic it should allow.
This is done using **Security Groups** in OpenStack.
In this example, we:
- Create a new security group
- Allow incoming **SSH** (port 22) traffic to enable remote access
- Allow incoming **ICMP** traffic so the VM can be pinged
- Allow incoming **HTTP** (port 80) traffic for hosting web services
_Note: OpenStack automatically allows all outgoing traffic (egress) from your VM._
%% Cell type:code id:aceddf23-baeb-4e86-8100-52beec7241a4 tags:
``` python
# Create a new security group
new_security_group = scoped_openstack_connection.create_security_group(
name="My VM Security Group",
description="Allows SSH, HTTP and ICMP traffic"
)
# Allow incoming SSH (port 22)
scoped_openstack_connection.create_security_group_rule(
new_security_group.id,
port_range_min=22,
port_range_max=22,
protocol="tcp",
direction="ingress",
description="Allow SSH"
)
# Allow incoming HTTP (port 80)
scoped_openstack_connection.network.create_security_group_rule(
security_group_id=new_security_group.id,
port_range_min=80,
port_range_max=80,
protocol="tcp",
direction="ingress",
description="Allow HTTP"
)
# Allow incoming ICMP (ping)
scoped_openstack_connection.create_security_group_rule(
new_security_group.id,
protocol="icmp",
direction="ingress",
description="Allow ICMP"
)
print(f"✅ Security group '{new_security_group.name}' created and configured.")
```
%% Output
✅ Security group 'My VM Security Group' created and configured.
%% Cell type:markdown id:94a9ee51-c6d7-4c35-b969-5097bb777f1c tags:
## 🔍 Select Resources for Your VM
Before launching a VM, you need to choose the following:
- **Image (OS)**: The base operating system your VM will use.
- **Flavor (CPU/RAM/Disk)**: Determines how much compute power (vCPUs), memory (RAM), and disk your VM will have.
📖 [Flavor details at PSNC](https://docs.cloud.psnc.pl/en/general/regions/bst-pl2-region/)
- **Network**: The virtual public network the VM will connect to, including internal/external IP options.
📖 [PSNC Network Design Overview](https://docs.psnc.pl/display/EOSCUserGuides/PSNC+Network+Design)
> 🛠️ Once selected, these options will be used to launch your VM using the OpenStack backend.
%% Cell type:code id:5e29662e-1576-48f8-9ae2-f9e2be0ffc60 tags:
``` python
def format_size_gb(size_bytes):
return f"{size_bytes / (1024 ** 3):.2f} GB"
def format_image_options(images):
return [
f"{image.name} (ID: {image.id}, Size: {format_size_gb(image.size)})"
for image in images
]
def format_generic_options(resources, label_attr='name', id_attr='id'):
return [f"{getattr(res, label_attr)} (ID: {getattr(res, id_attr)})" for res in resources]
images = list(scoped_openstack_connection.compute.images())
flavors = list(scoped_openstack_connection.compute.flavors())
networks = list(scoped_openstack_connection.network.networks())
display(Markdown("### Available Images"))
image_dropdown = widgets.Dropdown(
options=format_image_options(images),
description='Image:',
layout=widgets.Layout(width='85%')
)
display(image_dropdown)
display(Markdown("### Available Flavors"))
flavor_dropdown = widgets.Dropdown(
options=format_generic_options(flavors),
description='Flavor:',
layout=widgets.Layout(width='85%')
)
display(flavor_dropdown)
display(Markdown("### Available Networks"))
network_dropdown = widgets.Dropdown(
options=format_generic_options(networks),
description='Network:',
layout=widgets.Layout(width='85%')
)
display(network_dropdown)
```
%% Output
### Available Images
### Available Flavors
### Available Networks
%% Cell type:code id:c10e65f1-1e6e-44f4-8e86-aa6e5452d10c tags:
``` python
def get_selected_name(dropdown_value):
return dropdown_value.split(" (ID: ")[0]
def get_selected_id(dropdown_value):
return dropdown_value.split(" (ID: ")[1].split(")")[0]
selected_image = get_selected_name(image_dropdown.value)
selected_flavor = get_selected_name(flavor_dropdown.value)
selected_network = get_selected_name(network_dropdown.value)
selected_network_id = get_selected_id(network_dropdown.value)
```
%% Cell type:code id:616210a6-a2f4-4732-bdbe-b37684d9dd74 tags:
``` python
print(f"Selected configuration: {selected_network}, {selected_flavor} and {selected_image}")
```
%% Output
Selected configuration: PSNC-EXT-PUB1-EDU, M1-NVME-2vCPU-8R-50D and ubuntu-24.04-x86_64-server-cloudimg-20241105
%% Cell type:markdown id:92b4ca77-ecc9-44f0-8464-5548b604e518 tags:
## 🌐 Set Up a Private Network Environment
You will also need to set up a **private network**, a **subnet**, and a **router** to manage traffic between your VMs and external networks. This process ensures that your VMs have both internal and external connectivity as required.
> Follow the steps below to configure your network:
%% Cell type:markdown id:e91b0bbe-8561-4bbd-87f7-927e53c35f68 tags:
<img src="figures/scheme.png" alt="Network scheme" width="400" height="200"/>
%% Cell type:markdown id:c3b75537-fb1b-448d-ad03-d637abb68b11 tags:
### Create Private Network
%% Cell type:code id:f9a37e64-ba85-43b3-b234-9ac09e743951 tags:
``` python
try:
print("Creating private network...")
network = scoped_openstack_connection.network.create_network(
name="My private network",
admin_state_up=True,
port_security_enabled=True
)
print(f"Created network: {network.name} (ID: {network.id})")
except SDKException as e:
print(f"Network creation failed: {e}")
raise
```
%% Output
Creating private network...
Created network: My private network (ID: fca7d4d2-4c2c-4a25-9ed8-9d65c9d0d52f)
%% Cell type:markdown id:cb79e628-f76c-442c-b7f0-72e4d74a98ac tags:
### Create Internal Network (subnet)
%% Cell type:code id:6a36a9da-14fc-41be-92a7-6e4b140c1d47 tags:
``` python
try:
print("Creating subnet...")
subnet = scoped_openstack_connection.network.create_subnet(
network_id=network.id,
name="My internal network",
ip_version=4,
cidr="192.168.1.0/24",
)
print(f"Created subnet: {subnet.name} (ID: {subnet.id})")
except SDKException as e:
print(f"Subnet creation failed: {e}")
raise
```
%% Output
Creating subnet...
Created subnet: My internal network (ID: 7558ff73-1885-4707-aa2e-bf9dbdd0fa19)
%% Cell type:markdown id:b6c347fc-c1a2-433e-9b71-4e20f93b9ac9 tags:
### Create Router and connect with network
%% Cell type:code id:1720eb87-7fb2-420b-bac4-2b9d8f5c0d31 tags:
``` python
try:
print(f"Creating router with external network: {selected_network}")
router = scoped_openstack_connection.network.create_router(
name="My router",
external_gateway_info={"network_id": selected_network_id}
)
print(f"Created router: {router.name} (ID: {router.id})")
# Connect private subnet to router
scoped_openstack_connection.network.add_interface_to_router(
router.id,
subnet_id=subnet.id
)
print(f"Connected subnet {subnet.name} to router {router.name}")
except SDKException as e:
print(f"Router setup failed: {e}")
raise
```
%% Output
Creating router with external network: PSNC-EXT-PUB1-EDU
Created router: My router (ID: 57abc611-b306-458e-bb17-cbdc49cdf9fb)
Connected subnet My internal network to router My router
%% Cell type:markdown id:3d1ab4c3-f811-459a-917b-4664f01253f7 tags:
![Network Topology](figures/network_topology.png)
> **Diagram**: OpenStack networking setup — private network connected with the external public network (e.g., `PSNC-EXT-PUB1-EDU`) by router.
%% Cell type:markdown id:7483a3d0-7894-46f6-b202-128ff92b7276 tags:
## 🚀 Launch VM
%% Cell type:code id:4cd699a0-ddd6-4356-b828-95673e15e405 tags:
``` python
# VM parameters
new_volume = scoped_openstack_connection.create_volume(10)
vm_name = "new_vm"
server = scoped_openstack_connection.create_server(
name=vm_name,
image=selected_image,
flavor=selected_flavor,
network=network, # Use new private network
key_name="key",
volumes=[new_volume],
security_groups=[new_security_group.name]
)
# Waiting until all is ready
time.sleep(10)
```
%% Cell type:code id:47ac6e4e-ee8d-413b-a7bf-a7f1562d01e7 tags:
``` python
# Refreshing VM object to get the current state
new_vm = scoped_openstack_connection.compute.find_server(server.id)
```
%% Cell type:markdown id:eeae3963-20cf-4283-a40c-c6ab54ebea7d tags:
### Configure floating IP associated with the VM for an external access
%% Cell type:code id:b61224ce-446d-42f7-b3d6-12e204ef26b5 tags:
``` python
new_fip = scoped_openstack_connection.create_floating_ip(network=selected_network)
```
%% Cell type:code id:d547d657-15d5-4c9f-86f8-2ffd6f2f3466 tags:
``` python
floating_ip_address = new_fip.floating_ip_address
print(f"Floating IP: {floating_ip_address}")
```
%% Output
Floating IP: 62.3.174.55
%% Cell type:markdown id:99447853-3a5e-445d-a690-8e034ef6156a tags:
> Next step is to add new FIP to new VM to get access through SSH.
%% Cell type:code id:8b06239e-fae7-464e-a887-5ce9c36b124c tags:
``` python
# add_ip_list requires actual address string
# or list of strings of address
scoped_openstack_connection.add_ip_list(new_vm, new_fip.floating_ip_address)
```
%% Output
openstack.compute.v2.server.Server(id=63542c19-0751-4b99-a42d-3d5cc22bb1a6, name=new_vm, status=BUILD, tenant_id=db6b4eb2d6fe454191e8d39b088564cd, user_id=0814e963545daece81ed1e1bda7b90ca33b58889e97e843aeb29ac634d15ae84, metadata={}, hostId=d54760d94ecc699c04cb77d71fa37050d12057e3460d7661477b5bf8, image={'id': '7816da3d-cc63-4f01-bc2a-832bf2391eb8', 'links': [{'rel': 'bookmark', 'href': 'https://claudius.cloud.psnc.pl:8774/images/7816da3d-cc63-4f01-bc2a-832bf2391eb8'}]}, flavor={'vcpus': 2, 'ram': 8192, 'disk': 50, 'ephemeral': 0, 'swap': 0, 'original_name': 'M1-NVME-2vCPU-8R-50D', 'extra_specs': {'aggregate_instance_extra_specs:nvme': 'general-purpose', 'quota:disk_read_bytes_sec': '524288000', 'quota:disk_read_iops_sec': '50000', 'quota:disk_write_bytes_sec': '524288000', 'quota:disk_write_iops_sec': '10000'}}, created=2025-04-14T08:26:48Z, updated=2025-04-14T08:26:52Z, addresses={}, accessIPv4=, accessIPv6=, links=[{'rel': 'self', 'href': 'https://claudius.cloud.psnc.pl:8774/v2.1/servers/63542c19-0751-4b99-a42d-3d5cc22bb1a6'}, {'rel': 'bookmark', 'href': 'https://claudius.cloud.psnc.pl:8774/servers/63542c19-0751-4b99-a42d-3d5cc22bb1a6'}], OS-DCF:diskConfig=MANUAL, progress=0, OS-EXT-AZ:availability_zone=BST0K10, pinned_availability_zone=BST0K10, config_drive=, key_name=key, OS-SRV-USG:launched_at=None, OS-SRV-USG:terminated_at=None, security_groups=[{'name': 'My VM Security Group'}], OS-EXT-STS:task_state=spawning, OS-EXT-STS:vm_state=building, OS-EXT-STS:power_state=0, os-extended-volumes:volumes_attached=[{'id': '6395a1f4-644e-4a6a-9d98-7892dba997b0', 'delete_on_termination': False}], locked=False, locked_reason=None, description=None, tags=[], trusted_image_certificates=None, OS-EXT-SRV-ATTR:hostname=new-vm, server_groups=[], location=Munch({'cloud': 'api.cloud.psnc.pl', 'region_name': None, 'zone': 'BST0K10', 'project': Munch({'id': 'db6b4eb2d6fe454191e8d39b088564cd', 'name': None, 'domain_id': None, 'domain_name': None})}))
%% Cell type:markdown id:9ac6fe06-da82-493b-a0ba-e6a4fd898a18 tags:
![Network Topology](figures/network_topology_vm.png)
> **Diagram**: After successful launching of VM, our network topology is extended with new created instance
%% Cell type:markdown id:ffd09a73-4a84-440e-8e8c-117b6b4163fc tags:
### 🔌 Connect to Your VM via SSH and configure web page
- Run a **cloud-init-like** script to:
- Update system packages
- Install **NGINX**
- Set up a simple homepage
%% Cell type:code id:43fbe01e-163a-44b1-ba1d-e7589562a918 tags:
``` python
print(f"Connecting to VM at {floating_ip_address}...")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(
hostname=floating_ip_address,
username="ubuntu",
key_filename="key.pem" # "/path/to/mykey.pem"
)
cloud_init_script = """
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y nginx
sudo bash -c "echo 'Hello from my VM' > /var/www/html/index.html"
"""
ssh.exec_command(cloud_init_script)
ssh.close()
except Exception as e:
print(f"SSH failed: {e}")
raise
print(f"Try accessing: http://{floating_ip_address}")
print(f"Access VM via ssh: ssh -i mykey.pem ubuntu@{floating_ip_address}")
```
%% Output
Connecting to VM at 62.3.174.55...
Try accessing: http://62.3.174.55
Access VM via ssh: ssh -i mykey.pem ubuntu@62.3.174.55
%% Cell type:markdown id:2e5e5e46-791d-406a-9f6f-be3d0c234b0d tags:
> It will take some time for script to finish, so the provided link will not be accessible instantly
%% Cell type:markdown id:dd588470-3fc1-4b41-9bba-c929743fa9c9 tags:
# Finish session
%% Cell type:markdown id:89a921c0-8591-447a-b08a-ade26b7256ab tags:
## Detach floating IP from current server
%% Cell type:code id:3cf9a669-0dba-42aa-be26-e9f804c35e48 tags:
``` python
scoped_openstack_connection.detach_ip_from_server(new_vm, new_fip.id)
```
%% Output
True
%% Cell type:markdown id:29e3db6c-5b15-4b32-ad7b-8356436733e7 tags:
## Backup your attached volume
%% Cell type:code id:95feded0-22be-464c-876e-39f1015a773e tags:
``` python
attached_volume = new_vm.attached_volumes[0]
# OpenStack recommends to suspend VM and then backup volume.
# You can use `force` parameter to bypass it.
scoped_openstack_connection.create_volume_backup(attached_volume.id, name="My Backup", force=True)
```
%% Output
openstack.block_storage.v3.backup.Backup(name=My Backup, volume_id=6395a1f4-644e-4a6a-9d98-7892dba997b0, description=None, force=True, is_incremental=False, snapshot_id=None, id=65fa853d-9dfd-4b67-85e8-c4e333a3107c, links=[{'rel': 'self', 'href': 'https://claudius.cloud.psnc.pl:8776/v3/db6b4eb2d6fe454191e8d39b088564cd/backups/65fa853d-9dfd-4b67-85e8-c4e333a3107c'}, {'rel': 'bookmark', 'href': 'https://claudius.cloud.psnc.pl:8776/db6b4eb2d6fe454191e8d39b088564cd/backups/65fa853d-9dfd-4b67-85e8-c4e333a3107c'}], status=available, size=10, object_count=205, availability_zone=BST, container=cinder-backup-s3-claudius, created_at=2025-04-14T08:45:35.000000, updated_at=2025-04-14T08:47:24.000000, fail_reason=None, has_dependent_backups=False, data_timestamp=2025-04-14T08:45:35.000000, metadata={}, location=Munch({'cloud': 'api.cloud.psnc.pl', 'region_name': None, 'zone': 'BST', 'project': Munch({'id': 'db6b4eb2d6fe454191e8d39b088564cd', 'name': None, 'domain_id': None, 'domain_name': None})}))
%% Cell type:markdown id:878db2a7-dbd3-41c8-b629-6ca2954381c6 tags:
## Release allocated resources
%% Cell type:code id:301cbef1-ec92-439f-8f2d-fca92e2c33a9 tags:
``` python
scoped_openstack_connection.delete_server(new_vm.id)
scoped_openstack_connection.delete_floating_ip(new_fip)
```
%% Output
True
%% Cell type:code id:dc283305-7e0e-4967-84e9-d1573b45136e tags:
``` python
# If you do not wish to use generated key-pair anymore
# you can simply delete it
scoped_openstack_connection.delete_keypair("mykey")
```
%% Output
False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment