Commit 97ac9277 authored by garciadav's avatar garciadav
Browse files

Update relations package and add cmr package

Related change in IM: https://osm.etsi.org/gerrit/#/c/osm/IM/+/11212/
parent 467f2307
Pipeline #2737 failed with stage
in 48 seconds
# sshproxy
## Description
SSHProxy Charm example for Open Source MANO.
The purpose of this charm is to operate a VNF via SSH. For this, the charm should know the hostname of the VNF (ip address), and the username. The charm will be in a blocked state until it has the hostname, username, and the credentials for SSH-ing to the VNF. Both hostname and username are set via config, with `ssh-hostname` and `ssh-username` respectively.
There are two ways of specifying the credentials: password, keys. (See next section)
## Usage
This charm works for both LXD and K8s. By default, it will work on LXD. To make it work in K8s, just change the following in the `metadata.yaml`
```yaml
series:
# - focal
# - bionic
# - xenial
- kubernetes
deployment:
mode: operator
```
### Prepare the environment:
- LXD:
```bash
sudo snap install juju --classic
juju bootstrap lxd
juju add-model test
```
- K8s:
```bash
sudo snap install juju --classic
sudo snap install microk8s --classic
sudo microk8s.status --wait-ready
sudo microk8s.enable storage dns
juju bootstrap microk8s
juju add-model test
```
### Deploying charm:
```bash
charmcraft build
juju deploy ./sshproxy.charm
```
### Configuring the charm:
First of all, set the username and hostname of the VNF:
```bash
juju config sshproxy ssh-hostname=<hostname> \
ssh-username=<username>
```
#### Mirrors
To workaround this [bug](https://bugs.launchpad.net/juju/+bug/1929399), use the following configurations of the charm to specify the urls of apt and security mirrors.
```bash
juju config sshproxy apt-mirror=<apt-mirror> \
security-apt-mirror=<security-apt-mirror>
```
### Credentials
There are two ways to set up the credentials for the charm to be able to SSH the VNF.
With password:
```bash
juju config sshproxy ssh-password=<password>
```
With public keys:
1. First get the public key from the charm
```bash
$ juju run-action sshproxy/0 get-ssh-public-key --wait
unit-sshproxy-0:
UnitId: sshproxy/0
id: "12"
results:
pubkey: |
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC0IgSXMsu/5tY9QjsQfiAqcs6MBMO/7BDJB1ohbJFfvFrPiGg3+5FohKOsgu3SPiZbBhTITmI1YdaiZK7Dye2tHUfQKXhPFVFFVRtyWG6U/QJqEn+6OvAhjladcBlah4cb//r8V2Lvk/PshBJPzgvYSNJOhMhpfAMc7SMMpS8VxGBDIuTE0JFlHcRALJy3YBg70DPea2xrLsxqfA2pTA33KcjK2GyLgfOcZavmkqudEA/WaFb0xCY16TB/hDQBSwZm3l2kJ2aHyAWyWMmLNGL0TT14HUQKR1a8NS/kAnKY/yQf04dKoicmCvl4B3ndIYFT5Pq9b07mVfZvEH5Blle/x48iUF6JHWH2383SXwZfGy3XcX+lRx3u+IIkzS/Pmgt175JVdpu8bktk1c3Ekc0aL9v1gJ8rmZo+C6cilBoaziPfbqIatGPeGxnTDdw0JSfpxUGIQF4H98VOdWf3cHGC1hJZubZt0MGYeK4bk7GfsVPMlGaBWDyTaBQ5d9dHGxJpJX5OMBCDD4MfBYvg9IlgsVr1vDbpB4OFoAmQqFgnUWxRg2w0Iv3HBeMCvvOMM7DBOjwgjgbwa693Oyt/Rxd3GOwvy6vRQkFTgVS6f69SyIMCj9aIl1zxkIcOsfM5aU6vgio1BVWt9Xrj1TA3dTYJ7fkC5LJetqJ1knO67u67ww== root@juju-73fac6-2
status: completed
timing:
completed: 2020-11-18 15:42:03 +0000 UTC
enqueued: 2020-11-18 15:42:00 +0000 UTC
started: 2020-11-18 15:42:03 +0000 UTC
```
2. Inject that key in `~/.ssh/authorized_keys` at the VNF
3. Verify the ssh credentials
```bash
$ juju run-action sshproxy/0 verify-ssh-credentials --wait
unit-sshproxy-0:
UnitId: sshproxy/0
id: "14"
results:
verified: "True"
status: completed
timing:
completed: 2020-11-18 15:39:30 +0000 UTC
enqueued: 2020-11-18 15:39:29 +0000 UTC
started: 2020-11-18 15:39:29 +0000 UTC
```
## Developing
Create and activate a virtualenv with the development requirements:
virtualenv -p python3 venv
source venv/bin/activate
pip install -r requirements-dev.txt
## Testing
The Python operator framework includes a very nice harness for testing
operator behaviour without full deployment. Just `run_tests`:
./run_tests
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
#
# This is only an example, and you should edit to suit your needs.
# If you don't need actions, you can remove the file entirely.
# It ties in to the example _on_fortune_action handler in src/charm.py
touch:
description: "Touch a file on the VNF."
params:
filename:
description: "The name of the file to touch."
type: string
default: ""
required:
- filename
# Standard OSM functions
start:
description: "Stop the service on the VNF."
stop:
description: "Stop the service on the VNF."
restart:
description: "Stop the service on the VNF."
reboot:
description: "Reboot the VNF virtual machine."
upgrade:
description: "Upgrade the software on the VNF."
# Required by charms.osm.sshproxy
run:
description: "Run an arbitrary command"
params:
command:
description: "The command to execute."
type: string
default: ""
required:
- command
generate-ssh-key:
description: "Generate a new SSH keypair for this unit. This will replace any existing previously generated keypair."
verify-ssh-credentials:
description: "Verify that this unit can authenticate with server specified by ssh-hostname and ssh-username."
get-ssh-public-key:
description: "Get the public SSH key for this unit."
# Copyright 2021 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact: legal@canonical.com
#
# To get in touch with the maintainers, please contact:
# osm-charmers@lists.launchpad.net
##
type: charm
bases:
- build-on:
- name: ubuntu
channel: "20.04"
architectures: ["amd64"]
run-on:
- name: ubuntu
channel: "20.04"
architectures: [amd64]
parts:
charm:
build-packages: [git]
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
#
# This is only an example, and you should edit to suit your needs.
# If you don't need config, you can remove the file entirely.
options:
ssh-hostname:
type: string
default: ""
description: "The hostname or IP address of the machine to"
ssh-username:
type: string
default: ""
description: "The username to login as."
ssh-password:
type: string
default: ""
description: "The password used to authenticate."
# ssh-private-key:
# type: string
# default: ""
# description: "DEPRECATED. The private ssh key to be used to authenticate."
ssh-public-key:
type: string
default: ""
description: "The public key of this unit."
ssh-key-type:
type: string
default: "rsa"
description: "The type of encryption to use for the SSH key."
ssh-key-bits:
type: int
default: 4096
description: "The number of bits to use for the SSH key."
apt-mirror:
type: string
description: "Apt mirror. Only used for K8s proxy charms with Juju version lower than 2.9.4"
security-apt-mirror:
type: string
description: "Security Apt mirror. Only used for K8s proxy charms with Juju version lower than 2.9.4"
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
name: sshproxy
description: |
This is a basic example of an SSHProxy Charm for OSM
summary: |
This charm should be used as a base for any Proxy Charm (SSH based)
series:
# - focal
# - bionic
# - xenial
- kubernetes
deployment:
mode: operator
peers:
proxypeer:
interface: proxypeer
provides:
interface:
interface: interface
tags:
- osm
- proxycharm
- ssh
ops
packaging
git+https://github.com/charmed-osm/charms.osm#egg=charms.osm
#!/bin/sh -e
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
if [ -z "$VIRTUAL_ENV" -a -d venv/ ]; then
. venv/bin/activate
fi
if [ -z "$PYTHONPATH" ]; then
export PYTHONPATH=src
else
export PYTHONPATH="src:$PYTHONPATH"
fi
flake8
python3 -m unittest -v "$@"
#!/usr/bin/env python3
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
from ops.main import main
from charms.osm.sshproxy import SSHProxyCharm
from ops.model import (
ActiveStatus,
MaintenanceStatus,
)
class SshproxyCharm(SSHProxyCharm):
def __init__(self, *args):
super().__init__(*args)
# An example of setting charm state
# that's persistent across events
self.state.set_default(is_started=False)
if not self.state.is_started:
self.state.is_started = True
# Register all of the events we want to observe
# Charm events
self.framework.observe(self.on.config_changed, self.on_config_changed)
self.framework.observe(self.on.install, self.on_install)
self.framework.observe(self.on.start, self.on_start)
self.framework.observe(self.on.upgrade_charm, self.on_upgrade_charm)
# Charm actions (primitives)
self.framework.observe(self.on.touch_action, self.on_touch_action)
# OSM actions (primitives)
self.framework.observe(self.on.start_action, self.on_start_action)
self.framework.observe(self.on.stop_action, self.on_stop_action)
self.framework.observe(self.on.restart_action, self.on_restart_action)
self.framework.observe(self.on.reboot_action, self.on_reboot_action)
self.framework.observe(self.on.upgrade_action, self.on_upgrade_action)
def on_config_changed(self, event):
"""Handle changes in configuration"""
super().on_config_changed(event)
def on_install(self, event):
super().on_install(event)
def on_start(self, event):
"""Called when the charm is being installed"""
super().on_start(event)
def on_upgrade_charm(self, event):
"""Upgrade the charm."""
self.unit.status = MaintenanceStatus("Upgrading charm")
# Do upgrade related stuff
self.unit.status = ActiveStatus("Active")
def on_touch_action(self, event):
"""Touch a file."""
if self.unit.is_leader():
filename = event.params["filename"]
proxy = self.get_ssh_proxy()
stdout, stderr = proxy.run("touch {}".format(filename))
proxy.scp("/etc/lsb-release", "/home/ubuntu/scp_file")
event.set_results({"output": stdout})
else:
event.fail("Unit is not leader")
return
###############
# OSM methods #
###############
def on_start_action(self, event):
"""Start the VNF service on the VM."""
pass
def on_stop_action(self, event):
"""Stop the VNF service on the VM."""
pass
def on_restart_action(self, event):
"""Restart the VNF service on the VM."""
pass
def on_reboot_action(self, event):
"""Reboot the VM."""
if self.unit.is_leader():
proxy = self.get_ssh_proxy()
stdout, stderr = proxy.run("sudo reboot")
if len(stderr):
event.fail(stderr)
else:
event.fail("Unit is not leader")
return
def on_upgrade_action(self, event):
"""Upgrade the VNF service on the VM."""
pass
if __name__ == "__main__":
main(SshproxyCharm)
# Copyright 2020 David Garcia
# See LICENSE file for licensing details.
import unittest
from unittest.mock import Mock, call
from ops.testing import Harness
from charm import SshproxyCharm
class TestCharm(unittest.TestCase):
@unittest.mock.patch("charms.osm.sshproxy.SSHProxy.run")
def test_touch_action(self, mock_ssh_proxy):
mock_ssh_proxy.return_value = ("stdout", None)
harness = Harness(SshproxyCharm)
harness.set_leader(is_leader=True)
harness.begin()
action_event = Mock(params={"filename": "/home/ubuntu/asd"})
harness.charm.on_touch_action(action_event)
self.assertTrue(action_event.set_results.called)
self.assertEqual(
action_event.set_results.call_args,
call({"output": "stdout"}),
)
def test_touch_action_fail(self):
harness = Harness(SshproxyCharm)
harness.set_leader(is_leader=False)
harness.begin()
action_event = Mock(params={"filename": "/home/ubuntu/asd"})
harness.charm.on_touch_action(action_event)
self.assertEqual(action_event.fail.call_args, [("Unit is not leader",)])
vnfd:
description:
A VNF consisting of 1 VDU connected to two external VL, and one for
data and another one for management
df:
- id: default-df
instantiation-level:
- id: default-instantiation-level
vdu-level:
- number-of-instances: 1
vdu-id: simple_requires
- number-of-instances: 1
vdu-id: simple_provides
vdu-profile:
- id: simple_requires
min-number-of-instances: 1
- id: simple_provides
min-number-of-instances: 1
lcm-operations-configuration:
operate-vnf-op-config:
day1-2:
- id: cross_model_no_relation-vnf
config-access:
ssh-access:
default-user: ubuntu
required: true
execution-environment-list:
- id: simple-provides-proxy-ee
juju:
charm: sshproxy.charm
proxy: true
cloud: k8s
external-connection-point-ref: provides-mgmt-ext
initial-config-primitive:
- seq: 1
name: config
execution-environment-ref: simple-provides-proxy-ee
parameter:
- name: ssh-hostname
value: <rw_mgmt_ip>
- name: ssh-username
value: ubuntu
- seq: 2
name: touch
execution-environment-ref: simple-provides-proxy-ee
parameter:
- data-type: STRING
name: filename
value: /home/ubuntu/first-touch
config-primitive:
- name: touch
execution-environment-ref: simple-provides-proxy-ee
parameter:
- data-type: STRING
default-value: /home/ubuntu/touched
name: filename
- id: simple_requires
config-access:
ssh-access:
default-user: ubuntu
required: true
execution-environment-list:
- id: simple-requires-ee
juju:
charm: simple-requires.charm
proxy: false
external-connection-point-ref: requires-mgmt-ext
initial-config-primitive:
- seq: 1
name: touch
execution-environment-ref: simple-requires-ee
parameter:
- data-type: STRING
name: filename
value: /home/ubuntu/first-touch
config-primitive:
- name: touch
execution-environment-ref: simple-requires-ee
parameter:
- data-type: STRING
default-value: /home/ubuntu/touched
name: filename
ext-cpd:
- id: requires-mgmt-ext
int-cpd:
cpd: simple_requires-eth0-int
vdu-id: simple_requires
- id: provides-mgmt-ext
int-cpd:
cpd: simple_provides-eth0-int
vdu-id: simple_provides
id: cross_model_no_relation-vnf
mgmt-cp: provides-mgmt-ext
product-name: cross_model_no_relation-vnf
sw-image-desc:
- id: ubuntu18.04
image: ubuntu18.04
name: ubuntu18.04
vdu:
- cloud-init-file: cloud-config.txt
id: simple_requires
int-cpd:
- id: simple_requires-eth0-int
virtual-network-interface-requirement:
- name: simple_requires-eth0
position: 1
virtual-interface:
type: PARAVIRT
name: simple_requires
sw-image-desc: ubuntu18.04
virtual-compute-desc: simple_requires-compute
virtual-storage-desc:
- simple_requires-storage
- cloud-init-file: cloud-config.txt
id: simple_provides
int-cpd:
- id: simple_provides-eth0-int
virtual-network-interface-requirement:
- name: simple_provides-eth0
position: 1
virtual-interface:
type: PARAVIRT
name: simple_provides
sw-image-desc: ubuntu18.04
virtual-compute-desc: simple_provides-compute
virtual-storage-desc:
- simple_provides-storage
version: 1.0
virtual-compute-desc:
- id: simple_requires-compute
virtual-cpu:
num-virtual-cpu: 1
virtual-memory:
size: 1.0
- id: simple_provides-compute
virtual-cpu:
num-virtual-cpu: 1
virtual-memory:
size: 1.0
virtual-storage-desc:
- id: simple_requires-storage
size-of-storage: 10
- id: simple_provides-storage
size-of-storage: 10
nsd:
nsd:
- description: NS with 2 VNFs with cloudinit connected by datanet and mgmtnet VLs
df:
- id: default-df
vnf-profile:
- id: "1"
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: "1"
constituent-cpd-id: provides-mgmt-ext
- constituent-base-element-id: "1"
constituent-cpd-id: requires-mgmt-ext
- constituent-base-element-id: "1"
constituent-cpd-id: requires-mgmt-kdu-ext
virtual-link-profile-id: mgmtnet
vnfd-id: cross_model_relation-vnf
- id: "2"
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: "1"
constituent-cpd-id: provides-mgmt-ext
- constituent-base-element-id: "1"
constituent-cpd-id: requires-mgmt-ext
virtual-link-profile-id: mgmtnet
vnfd-id: cross_model_no_relation-vnf
- id: "3"
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: "1"
constituent-cpd-id: provides-mgmt-ext
- constituent-base-element-id: "1"
constituent-cpd-id: requires-mgmt-ext
virtual-link-profile-id: mgmtnet
vnfd-id: cross_model_no_relation-vnf
id: cross_model_relation-ns
name: cross_model_relation-ns
version: "1.0"
virtual-link-desc:
- id: mgmtnet
mgmt-network: "true"
vnfd-id:
- cross_model_relation-vnf
- cross_model_no_relation-vnf
ns-configuration:
relation:
- name: relation
provider:
vnf-profile-id: "2"
endpoint: interface
requirer:
vnf-profile-id: "3"
vdu-profile-id: simple_requires
endpoint: interface
##
# Copyright 2020 Canonical Ltd.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
##
touch:
description: "Touch a file on the VNF."
params:
filename:
description: "The name of the file to touch."
type: string
default: ""
required:
- filename
# Standard OSM functions
start:
description: "Stop the service on the VNF."
stop:
description: "Stop the service on the VNF."
restart:
description: "Stop the service on the VNF."
reboot:
description: "Reboot the VNF virtual machine."
upgrade:
description: "Upgrade the software on the VNF."
# Required by charms.osm.sshproxy
run:
description: "Run an arbitrary command"
params:
command:
description: "The command to execute."
type: string
default: ""
required:
- command
generate-ssh-key:
description: "Generate a new SSH keypair for this unit. This will replace any existing previously generated keypair."
verify-ssh-credentials:
description: "Verify that this unit can authenticate with server specified by ssh-hostname and ssh-username."
get-ssh-public-key:
description: "Get the public SSH key for this unit."
##
# Copyright 2020 Canonical Ltd.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
##
options:
ssh-hostname:
type: string
default: ""
description: "The hostname or IP address of the machine to"
ssh-username:
type: string
default: ""
description: "The username to login as."
ssh-password:
type: string
default: ""
description: "The password used to authenticate."
ssh-public-key:
type: string
default: ""
description: "The public key of this unit."
ssh-key-type:
type: string
default: "rsa"
description: "The type of encryption to use for the SSH key."
ssh-key-bits:
type: int
default: 4096
description: "The number of bits to use for the SSH key."
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment