diff --git a/03-day1.md b/03-day1.md index c05aed172b4cf137f47f0d9f5fd259a60d489493..272066539ac6ae6da9a82be8fae1edcd96059cdb 100644 --- a/03-day1.md +++ b/03-day1.md @@ -4,9 +4,20 @@ The objective of this section is to provide the guidelines to include all necessary elements in the VNF Package. This allows the exposed services inside the VNF to be automatically initialized right after the VNF instantiation. -The main mechanism to achieve this in OSM is to build a Proxy Charm and include it in the descriptor. +The main mechanism to achieve this in OSM is to build a Charm and include it in the descriptor. -## Day-1 Onboarding Guidelines +In the VNFD you will find metadata, which is declarative data specified in the YAML file, and code that takes care of the operations related to a VNF. The operations code is call "Charm", and it can handle the lifecycle, configuration, integration, and actions/primitives in your workloads. + +There are two kinds of Charms, and at this point you have to decide which one you need, and that depends on the nature of your workload. These are the two types of Charms: + +- Proxy Charms +- Native Charms + +If you are using a fixed image for your workload, which CANNOT be modified, then the Charm has to be allocated not in the workload **(Proxy Charm)**. For those cases, the VCA (VNF Configuration and Abstraction) has an LXD and Kubernetes clouds available in which the Charm will live. + +However, if the the workload CAN be modified, then the code can live in the same workload (Native Charm). + +## Proxy Charms ### Adding Day-1 primitives to the descriptor @@ -19,13 +30,25 @@ This type of initial actions will run automatically after instantiation and shou * `ssh-hostname`: Typically used with the , which is automatically replaced by the VNF or VDU management IP address specified in the correspondent section. * `ssh-username`: The username used for authentication with the VDU. -* `ssh-password`: A static password (not recommended unless it is changed afterwards), otherwise if not defined, OSM will insert the public-keys of VCA. + +Additionally, OSM VCA needs the credentials to succeed the authentication. For that, there are two options: +* Add `ssh-password` in the config initial-config-primitive: A static password +* Add `config-access in the vnf/vdu-configuration: With this method, OSM will inject the public keys generated by the Proxy Charm to the workload. +``` + vnf-configuration: + config-access: + ssh-access: + default-user: ubuntu + required: true +``` + +> NOTE: Any Charm can provide a set of configuration parameters in a config.yaml file. The value for those parameters should be specified in the `config` initial primitive. Additional to the *config primitive*, more initial primitives can be run in the desired order so that the VNF initializes its services. Note that each of these additional actions will be later detailed in the proxy charm that implements them. The following example shows VNF-level initial primitives: both the expected *config* primitive in the beginning, but also the *configure-remote* and *start-service* to be run in addition right after initialization. -``` +```yaml vnfd:vnfd-catalog: vnfd: - ... @@ -40,8 +63,8 @@ vnfd:vnfd-catalog: value: - name: ssh-username value: admin - - name: ssh-password - value: secretpassword + # - name: ssh-password + # value: secretpassword seq: '1' - name: configure-remote parameter: @@ -68,7 +91,8 @@ vnfd:vnfd-catalog: ... vdu-configuration: initial-config-primitive: - - name: config + - seq: '1' + name: config parameter: - name: ssh-hostname value: @@ -76,14 +100,13 @@ vnfd:vnfd-catalog: value: admin - name: ssh-password value: - seq: '1' - - name: configure-remote + - seq: '2' + name: configure-remote parameter: - name: dest-ip value: - seq: '2' - - name: start-service - seq: '3' + - seq: '3' + name: start-service juju: charm: samplecharm ``` @@ -100,11 +123,248 @@ Remember that when dealing with multiple variables, it might be useful to pass a osm ns-create ... --config-file vars.yaml ``` -### Building a Proxy Charm +### Create a Proxy Charm + +#### New operator framework (Recommended) + +This section will focus the attention on creating a Proxy charm to configure a workload. + +Create a folder for the `samplecharm` in the VNFD directory, and create necessary files: + +> Go to [charms.osm](https://github.com/charmed-osm/charms.osm) if you want to learn more about how to use the charms.osm library. + +```bash +mkdir -p charms/samplecharm/ +cd charms/samplecharm/ +mkdir hooks lib mod src +touch src/charm.py +touch actions.yaml metadata.yaml config.yaml +chmod +x src/charm.py +ln -s ../src/charm.py hooks/upgrade-charm +ln -s ../src/charm.py hooks/install +ln -s ../src/charm.py hooks/start +git clone https://github.com/canonical/operator mod/operator +git clone https://github.com/charmed-osm/charms.osm mod/charms.osm +ln -s ../mod/operator/ops lib/ops +ln -s ../mod/charms.osm/charms lib/charms +``` + +Include the following high level metadata in `metadata.yaml`: + +```yaml +name: samplecharm +summary: this is an example +maintainer: David Garcia +description: | + This is an example of a proxy charm deployed by Open Source Mano. +tags: + - nfv +subordinate: false +series: + - bionic + - xenial +peers: # This will give HA capabilities to your Proxy Charm + proxypeer: + interface: proxypeer +``` + +Add the following configuration parameters in `config.yaml`: + +```yaml +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." +``` + +Add the following actions in `actions.yaml` (the implementation will be done in src/charm.py): + +```yaml +# Actions to be implemented in src/charm.py +configure-remote: + description: "Configures the remote server" + params: + destination_ip: + description: "IP of the remote server" + type: string + default: "" + required: + - destination_ip +start-service: + description: "Starts the service of 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." +``` + +Add the following code to `src/charm.py`, which will implement the Day-1 primitives: +> Note: Actions in the Charm can be used in the VNFD for either Day-1 and Day-2 primitives. There's no difference in the Charm. + +```python +#!/usr/bin/env python3 +import sys + +sys.path.append("lib") + +from charms.osm.sshproxy import SSHProxyCharm +from ops.main import main + + +class SampleProxyCharm(SSHProxyCharm): + def __init__(self, framework, key): + super().__init__(framework, key) + + # Listen to 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) + + # Listen to the touch action event + self.framework.observe(self.on.configure_remote_action, self.configure_remote) + self.framework.observe(self.on.start_service_action, self.start_service) + + def on_config_changed(self, event): + """Handle changes in configuration""" + super().on_config_changed(event) + + def on_install(self, event): + """Called when the charm is being installed""" + super().on_install(event) + + def on_start(self, event): + """Called when the charm is being started""" + super().on_start(event) + + def configure_remote(self, event): + """Configure remote action.""" + + if self.model.unit.is_leader(): + stderr = None + try: + mgmt_ip = self.model.config["ssh-hostname"] + destination_ip = event.params["destination_ip"] + cmd = "vnfcli set license {} server {}".format( + mgmt_ip, + destination_ip + ) + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run(cmd) + event.set_results({"output": stdout}) + except Exception as e: + event.fail("Action failed {}. Stderr: {}".format(e, stderr)) + else: + event.fail("Unit is not leader") + + def start_service(self, event): + """Start service action.""" + + if self.model.unit.is_leader(): + stderr = None + try: + cmd = "sudo service vnfoper start" + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run(cmd) + event.set_results({"output": stdout}) + except Exception as e: + event.fail("Action failed {}. Stderr: {}".format(e, stderr)) + else: + event.fail("Unit is not leader") + + +if __name__ == "__main__": + main(SampleProxyCharm) + +``` + +As you can see, the Charm is pure Python code, which makes it very easy to develop. There are a few things that need explanation from the code above: + +```python +from charms.osm.sshproxy import SSHProxyCharm + +class SampleProxyCharm(SSHProxyCharm): +``` + +In the charms.osm library, you can find an SSHProxyCharm library that handles scalability of proxy charms, and many other actions needed particularly in Proxy Charms. It is recommendable to use that class as the base class for any Proxy Charm. + +```python + def __init__(self, framework, key): + super().__init__(framework, key) + + # Listen to 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) + + # Listen to the touch action event + self.framework.observe(self.on.configure_remote_action, self.configure_remote) + self.framework.observe(self.on.start_service_action, self.start_service) +``` -The charm is the element that implements the primitives or, put in other words, it provides the logic for the different configurations on the VNF. It is defined inside the "charms" folder of the VNF package. The contents of the charm must be built from a Linux machine. +In the initialization of the Charm, we need to observe to start (self.on.start), install(self.on.install), and config_changed (self.on.config_changed) events. Additionally, we need to observe the events for the implemented actions, which have the following format: self.on._action. -#### Method 1: Building a Proxy Charm the traditional way +```python + def on_config_changed(self, event): + """Handle changes in configuration""" + super().on_config_changed(event) + + def on_install(self, event): + """Called when the charm is being installed""" + super().on_install(event) + + def on_start(self, event): + """Called when the charm is being started""" + super().on_start(event) +``` + +This functions will be called in config_change, install, and start events respectively. The methods implemented by the SSHProxyCharm class are called because it handles some important things related to ProxyCharms. But after calling the super() methods you can write code in each one of those events. + +```python + def configure_remote(self, event): + ... + def start_service(self, event): + ... +``` + +Finally, we defined the functions for the actions. + +#### DEPRECATED: Reactive (Method 1): Building a Proxy Charm the traditional way a) Install the charm tools and setup your environment. You might want to copy the `export` lines to your "~/.bashrc" profile file to automatically load them in the next session. @@ -269,7 +529,7 @@ h) Finally, build the charm with `charm build` and copy the resulting folder (in Futher information about building charms can be found [here](https://osm.etsi.org/wikipub/index.php/Creating_your_own_VNF_charm_(Release_THREE)). -#### Method 2: Using Proxy Charm Generators +#### DEPRECATED: Reactive (Method 2): Using Proxy Charm Generators To date, the only supported generator is Ansible, which means that a Proxy Charm can be automatically populated based on an Ansible Playbook. @@ -341,6 +601,7 @@ Once the VNF is launched, the results from running the generator will be found i **Note**: some VNFs will not pass some SSH pre-checks that Ansible performs in some operations (SFTP, SCP, etc.) In those cases, it has been noted that `ansible_connection=ssh`, which is a default set of the generator, needs to be disabled. This preset would need to be deleted from the `lib/charms/libansible.py` file, `create_hosts` function. `[TODO: explore an enhancement to the Ansible Generator, to be as generic as possible]` + ### Testing Instantiation of the VNF Package Remember the objective of this phase: **to configure the VNF automatically so it starts providing the expected service**. @@ -372,3 +633,23 @@ After deployment is done, proxy charms can be monitored and debugged by using th Since Release 6, every NS has its own juju instance (model) for its charms, so before running juju commands, you need to switch to the right model using `juju switch [NS ID]` If proxy charms need to be started at any particular order, please note that the order of proxy charm initialization follows the order in which 'constituent VNFs' are listed at the NSD, but the actual operations could be executed in a different order, depending on the time it takes for each proxy charm container to be ready. + +## Scaling proxy charms + +In the additional configuration parameters, there is a `config-units` option that will scale the Proxy Charms you want. + +```yaml +additionalParamsForVnf: + - member-vnf-index: "1" + config-units: 2 +``` + +## Native Charms (In progress) + + diff --git a/export.sh b/export.sh index 077efda94a5cb67140f9efafc2abd6c71ab74fe7..3adc7f67ede11539517f3b54ef4ea41743c1616d 100755 --- a/export.sh +++ b/export.sh @@ -6,7 +6,7 @@ template=${1} timestamp=$(date "+%Y.%m.%d-%H.%M.%S") documents="00-introduction.md 01-requirements.md 02-day0.md \ -03-day1.md 04-day2.md 05-knownissues.md 06-walkthrough.md" +03-day1.md 04-day2.md 05-basic-examples.md 06-walkthrough.md" if [ -z "$template" ]; then name=output-$timestamp.html