diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..5625aa190458d76d915eaabc3c80fff7d0510d86 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,18 @@ +[submodule "magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/mod/operator"] + path = magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/mod/operator + url = https://github.com/canonical/operator +[submodule "magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/operator"] + path = magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/operator + url = https://github.com/canonical/operator +[submodule "magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/charms.osm"] + path = magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/charms.osm + url = https://github.com/charmed-osm/charms.osm +[submodule "magma/hackfest_gateway_pnf/charms/vyos-config/mod/charms.osm"] + path = magma/hackfest_gateway_pnf/charms/vyos-config/mod/charms.osm + url = https://github.com/charmed-osm/charms.osm +[submodule "magma/hackfest_gateway_pnf/charms/vyos-config/mod/operator"] + path = magma/hackfest_gateway_pnf/charms/vyos-config/mod/operator + url = https://github.com/canonical/operator +[submodule "magma/hackfest_gateway_pnf/charms/vyos-config/mod/charm-helpers"] + path = magma/hackfest_gateway_pnf/charms/vyos-config/mod/charm-helpers + url = https://github.com/juju/charm-helpers.git diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config b/magma/hackfest_gateway_pnf/charms/vyos-config deleted file mode 160000 index 6785ab97fda26956af0891589ee0e3e3f45c53cc..0000000000000000000000000000000000000000 --- a/magma/hackfest_gateway_pnf/charms/vyos-config +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6785ab97fda26956af0891589ee0e3e3f45c53cc diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/README.md b/magma/hackfest_gateway_pnf/charms/vyos-config/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2bdcab20c1a6d39aaffe9a450ba3bdbc44044b42 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/README.md @@ -0,0 +1,3 @@ +# Vyos-config + +This is a proxy charm used by Open Source Mano (OSM) to configure Vyos Router PNF, written in the [Python Operator Framwork](https://github.com/canonical/operator) diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/actions.yaml b/magma/hackfest_gateway_pnf/charms/vyos-config/actions.yaml new file mode 100644 index 0000000000000000000000000000000000000000..26b53376530cdd99cb10196e0ee5f7242c05207e --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/actions.yaml @@ -0,0 +1,39 @@ +# VyOS Action +configure-remote: + description: "Add firewall rule to VyOS PNF." + params: + magmaIP: + description: "Magma AGW allowed IP" + type: "string" + default": "0.0.0.0" + required: + - magmaIP + +# 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." diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/config.yaml b/magma/hackfest_gateway_pnf/charms/vyos-config/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5b908ae0a19da5d540c59d887ab4ca7939ebdd08 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/config.yaml @@ -0,0 +1,29 @@ +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." diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/hooks/install b/magma/hackfest_gateway_pnf/charms/vyos-config/hooks/install new file mode 120000 index 0000000000000000000000000000000000000000..25b1f68fa39d58d33c08ca420c3d439d19be0c55 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/hooks/install @@ -0,0 +1 @@ +../src/charm.py \ No newline at end of file diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charmhelpers b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charmhelpers new file mode 120000 index 0000000000000000000000000000000000000000..cedc09e9124f7168aa9a7decc8a5129293eb88a6 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charmhelpers @@ -0,0 +1 @@ +../mod/charm-helpers/charmhelpers \ No newline at end of file diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charms/osm b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charms/osm new file mode 120000 index 0000000000000000000000000000000000000000..385b019dc112a34c6c14d2f77fd25d1d94551ff7 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/charms/osm @@ -0,0 +1 @@ +../../mod/charms.osm/charms/osm \ No newline at end of file diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/lib/ops b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/ops new file mode 120000 index 0000000000000000000000000000000000000000..d93419320c2e0d3133a0bc8059e2f259bf5bb213 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/lib/ops @@ -0,0 +1 @@ +../mod/operator/ops \ No newline at end of file diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/metadata.yaml b/magma/hackfest_gateway_pnf/charms/vyos-config/metadata.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c331b60d88dcfc3f1c675e24282dacdf8ec4ffdb --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/metadata.yaml @@ -0,0 +1,11 @@ +name: vyos-config +summary: A proxy charm to configure VyOS Router +maintainer: David García +description: | + Charm to configure VyOS PNF +series: + - xenial + - bionic +peers: + proxypeer: + interface: proxypeer diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charm-helpers b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charm-helpers new file mode 160000 index 0000000000000000000000000000000000000000..8a90d2cc3aa0ba170756bdc87b4499903ac86fe5 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charm-helpers @@ -0,0 +1 @@ +Subproject commit 8a90d2cc3aa0ba170756bdc87b4499903ac86fe5 diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charms.osm b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charms.osm new file mode 160000 index 0000000000000000000000000000000000000000..cf167cf041a3d4ca9b8dd90e9f575c9c4e99b8ad --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/charms.osm @@ -0,0 +1 @@ +Subproject commit cf167cf041a3d4ca9b8dd90e9f575c9c4e99b8ad diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/mod/operator b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/operator new file mode 160000 index 0000000000000000000000000000000000000000..a3918e1db40edac88a5250c46d7ef3156e8853d3 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/mod/operator @@ -0,0 +1 @@ +Subproject commit a3918e1db40edac88a5250c46d7ef3156e8853d3 diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/playbooks/configure-remote.yaml b/magma/hackfest_gateway_pnf/charms/vyos-config/playbooks/configure-remote.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cc0232fae05dca7c81301a771a07b133f006a75c --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/playbooks/configure-remote.yaml @@ -0,0 +1,8 @@ +- hosts: vyos-routers + connection: local + tasks: + - name: add a host to the list of allowed in the firewall group + vyos_config: + lines: + - set firewall group network-group MAGMA_AGW network "{{ MAGMA_AGW_IP }}/32" + diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/requirements.txt b/magma/hackfest_gateway_pnf/charms/vyos-config/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/src/charm.py b/magma/hackfest_gateway_pnf/charms/vyos-config/src/charm.py new file mode 100755 index 0000000000000000000000000000000000000000..2b9ce7d4dfdfb03455271aa1ea2a125228c91b25 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/src/charm.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +# Copyright 2020 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. + +import sys + +sys.path.append("lib") + +from ops.charm import CharmBase, CharmEvents +from ops.framework import StoredState, EventBase, EventSource +from ops.main import main +from ops.model import ( + ActiveStatus, + BlockedStatus, + MaintenanceStatus, + WaitingStatus, + ModelError, +) +import os +import subprocess +import traceback +from proxy_cluster import ProxyCluster + +from charms.osm.sshproxy import SSHProxy +from charms.osm import libansible + + +class SSHKeysInitialized(EventBase): + def __init__(self, handle, ssh_public_key, ssh_private_key): + super().__init__(handle) + self.ssh_public_key = ssh_public_key + self.ssh_private_key = ssh_private_key + + def snapshot(self): + return { + "ssh_public_key": self.ssh_public_key, + "ssh_private_key": self.ssh_private_key, + } + + def restore(self, snapshot): + self.ssh_public_key = snapshot["ssh_public_key"] + self.ssh_private_key = snapshot["ssh_private_key"] + + +class ProxyClusterEvents(CharmEvents): + ssh_keys_initialized = EventSource(SSHKeysInitialized) + + +class SimpleHAProxyCharm(CharmBase): + + state = StoredState() + on = ProxyClusterEvents() + + def __init__(self, framework, key): + super().__init__(framework, key) + + # An example of setting charm state + # that's persistent across events + self.state.set_default(is_started=False) + + self.peers = ProxyCluster(self, "proxypeer") + + if not self.state.is_started: + self.state.is_started = True + + # Register all of the events we want to observe + for event in ( + # Charm events + self.on.config_changed, + self.on.install, + self.on.start, + self.on.upgrade_charm, + # Charm actions (primitives) + self.on.configure_remote_action, + # OSM actions (primitives) + self.on.start_action, + self.on.stop_action, + self.on.restart_action, + self.on.reboot_action, + self.on.upgrade_action, + # SSH Proxy actions (primitives) + self.on.generate_ssh_key_action, + self.on.get_ssh_public_key_action, + self.on.run_action, + self.on.verify_ssh_credentials_action, + ): + self.framework.observe(event, self) + + self.framework.observe(self.on.proxypeer_relation_changed, self) + + def get_ssh_proxy(self): + """Get the SSHProxy instance""" + proxy = SSHProxy( + hostname=self.model.config["ssh-hostname"], + username=self.model.config["ssh-username"], + password=self.model.config["ssh-password"], + ) + return proxy + + def on_proxypeer_relation_changed(self, event): + if self.peers.is_cluster_initialized: + pubkey = self.peers.ssh_public_key + privkey = self.peers.ssh_private_key + SSHProxy.write_ssh_keys(public=pubkey, private=privkey) + self.on_config_changed(event) + else: + event.defer() + + def on_config_changed(self, event): + """Handle changes in configuration""" + unit = self.model.unit + + # Unit should go into a waiting state until verify_ssh_credentials is successful + unit.status = WaitingStatus("Waiting for SSH credentials") + proxy = self.get_ssh_proxy() + + verified = proxy.verify_credentials() + if verified: + unit.status = ActiveStatus() + else: + unit.status = BlockedStatus("Invalid SSH credentials.") + + def on_install(self, event): + unit = self.model.unit + unit.status = MaintenanceStatus("Installing Ansible") + libansible.install_ansible_support() + unit.status = ActiveStatus() + + def on_start(self, event): + """Called when the charm is being installed""" + if not self.peers.is_joined: + event.defer() + return + + unit = self.model.unit + + if not SSHProxy.has_ssh_key(): + unit.status = MaintenanceStatus("Generating SSH keys...") + pubkey = None + privkey = None + if self.is_leader: + if self.peers.is_cluster_initialized: + SSHProxy.write_ssh_keys( + public=self.peers.ssh_public_key, + private=self.peers.ssh_private_key, + ) + else: + SSHProxy.generate_ssh_key() + self.on.ssh_keys_initialized.emit( + SSHProxy.get_ssh_public_key(), SSHProxy.get_ssh_private_key() + ) + unit.status = ActiveStatus() + else: + unit.status = WaitingStatus("Waiting for leader to populate the keys") + + def on_configure_remote_action(self, event): + """Configure remote.""" + + if self.is_leader: + try: + config = self.model.config + magmaIP = event.params["magmaIP"] + dict_vars = {"MAGMA_AGW_IP": magmaIP} + proxy = self.get_ssh_proxy() + result = libansible.execute_playbook( + "configure-remote.yaml", + config["ssh-hostname"], + config["ssh-username"], + config["ssh-password"], + dict_vars, + ) + event.set_results({"output": result}) + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + err = traceback.format_exception(exc_type, exc_value, exc_traceback) + event.fail(message="configure-remote failed: " + str(err)) + + else: + event.fail("Unit is not leader") + return + + def on_upgrade_charm(self, event): + """Upgrade the charm.""" + unit = self.model.unit + + # Mark the unit as under Maintenance. + unit.status = MaintenanceStatus("Upgrading charm") + + self.on_install(event) + + # When maintenance is done, return to an Active state + unit.status = ActiveStatus() + + ############### + # 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.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 + + ##################### + # SSH Proxy methods # + ##################### + def on_generate_ssh_key_action(self, event): + """Generate a new SSH keypair for this unit.""" + if self.is_leader: + if not SSHProxy.generate_ssh_key(): + event.fail("Unable to generate ssh key") + else: + event.fail("Unit is not leader") + return + + def on_get_ssh_public_key_action(self, event): + """Get the SSH public key for this unit.""" + if self.is_leader: + pubkey = SSHProxy.get_ssh_public_key() + event.set_results({"pubkey": SSHProxy.get_ssh_public_key()}) + else: + event.fail("Unit is not leader") + return + + def on_run_action(self, event): + """Run an arbitrary command on the remote host.""" + if self.is_leader: + cmd = event.params["command"] + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run(cmd) + event.set_results({"output": stdout}) + if len(stderr): + event.fail(stderr) + else: + event.fail("Unit is not leader") + return + + def on_verify_ssh_credentials_action(self, event): + """Verify the SSH credentials for this unit.""" + if self.is_leader: + proxy = self.get_ssh_proxy() + + verified = proxy.verify_credentials() + if verified: + print("Verified!") + event.set_results({"verified": True}) + else: + print("Verification failed!") + event.set_results({"verified": False}) + else: + event.fail("Unit is not leader") + return + + @property + def is_leader(self): + # update the framework to include self.unit.is_leader() + return self.model.unit.is_leader() + + +class LeadershipError(ModelError): + def __init__(self): + super().__init__("not leader") + + +if __name__ == "__main__": + main(SimpleHAProxyCharm) diff --git a/magma/hackfest_gateway_pnf/charms/vyos-config/src/proxy_cluster.py b/magma/hackfest_gateway_pnf/charms/vyos-config/src/proxy_cluster.py new file mode 100644 index 0000000000000000000000000000000000000000..93cd119d4c92271078bf9b85fbe86eca18b415a2 --- /dev/null +++ b/magma/hackfest_gateway_pnf/charms/vyos-config/src/proxy_cluster.py @@ -0,0 +1,71 @@ +# Copyright 2020 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. + +from ops.framework import Object, StoredState + + +class ProxyCluster(Object): + + state = StoredState() + + def __init__(self, charm, relation_name): + super().__init__(charm, relation_name) + self._relation_name = relation_name + self._relation = self.framework.model.get_relation(self._relation_name) + + self.framework.observe(charm.on.ssh_keys_initialized, self) + + self.state.set_default(ssh_public_key=None) + self.state.set_default(ssh_private_key=None) + + def on_ssh_keys_initialized(self, event): + if not self.framework.model.unit.is_leader(): + raise RuntimeError("The initial unit of a cluster must also be a leader.") + + self.state.ssh_public_key = event.ssh_public_key + self.state.ssh_private_key = event.ssh_private_key + if not self.is_joined: + event.defer() + return + + self._relation.data[self.model.app][ + "ssh_public_key" + ] = self.state.ssh_public_key + self._relation.data[self.model.app][ + "ssh_private_key" + ] = self.state.ssh_private_key + + @property + def is_joined(self): + return self._relation is not None + + @property + def ssh_public_key(self): + if self.is_joined: + return self._relation.data[self.model.app].get("ssh_public_key") + + @property + def ssh_private_key(self): + if self.is_joined: + return self._relation.data[self.model.app].get("ssh_private_key") + + @property + def is_cluster_initialized(self): + return ( + True + if self.is_joined + and self._relation.data[self.model.app].get("ssh_public_key") + and self._relation.data[self.model.app].get("ssh_private_key") + else False + ) diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/README.md b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f5595f2db99715ce807f361e0fa257f8f91c2a4e --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/README.md @@ -0,0 +1,3 @@ +# EnodeB Charm + +This is a native charm used by Open Source Mano (OSM) to add day-2 primitive to an enodeB + radio emulator, written in the [Python Operator Framwork](https://github.com/canonical/operator) diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/actions.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/actions.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c578a9c94a580a6a4387ad21ae92679c56f3f881 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/actions.yaml @@ -0,0 +1,38 @@ +register: + description: "Register to AGW (EPC)." + params: + mme-addr: + description: "MME address." + type: string + default: "" + gtp-bind-addr: + description: "GTP bind address" + type: string + default: "" + s1c-bind-addr: + description: "S1C bind address." + type: string + default: "" + required: + - mme-addr + - gtp-bind-addr + - s1c-bind-addr +attach-ue: + description: Attach User Emulator to enodeB + params: + usim-imsi: + description: "USIM IMSI" + type: string + default: "" + usim-k: + description: "USIM K" + type: string + default: "" + usim-opc: + description: "USIM OPC" + type: string + default: "" + required: + - usim-imsi + - usim-k + - usim-opc diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/config.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a985d46205251d796558d12d48462bbc673b5133 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/config.yaml @@ -0,0 +1 @@ +options: {} diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/hooks/install b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/hooks/install new file mode 120000 index 0000000000000000000000000000000000000000..25b1f68fa39d58d33c08ca420c3d439d19be0c55 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/hooks/install @@ -0,0 +1 @@ +../src/charm.py \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/lib/ops b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/lib/ops new file mode 120000 index 0000000000000000000000000000000000000000..d93419320c2e0d3133a0bc8059e2f259bf5bb213 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/lib/ops @@ -0,0 +1 @@ +../mod/operator/ops \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/metadata.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/metadata.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1d07967ff6f8fe5f5447d497bb776ca4b476fdeb --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/metadata.yaml @@ -0,0 +1,8 @@ +name: enodeb +summary: A native charm for Enodeb +maintainer: David García +description: | + A charm to add day-2 primitive to an enodeb and radio simulator VDU +series: + - xenial + - bionic diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/mod/operator b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/mod/operator new file mode 160000 index 0000000000000000000000000000000000000000..a3918e1db40edac88a5250c46d7ef3156e8853d3 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/mod/operator @@ -0,0 +1 @@ +Subproject commit a3918e1db40edac88a5250c46d7ef3156e8853d3 diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/src/charm.py b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/src/charm.py new file mode 100755 index 0000000000000000000000000000000000000000..ab52f0675de1948f56041502e799e60e318d514a --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/enodeb/src/charm.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# Copyright 2020 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. + +import sys + +sys.path.append("lib") + +from ops.charm import CharmBase +from ops.framework import StoredState +from ops.main import main +from ops.model import ( + ActiveStatus, + BlockedStatus, + MaintenanceStatus, + WaitingStatus, + ModelError, +) +import subprocess + + +class EnodebCharm(CharmBase): + state = StoredState() + + 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 + for event in ( + # Charm events + self.on.config_changed, + self.on.install, + self.on.upgrade_charm, + self.on.register_action, + self.on.attach_ue_action, + ): + self.framework.observe(event, self) + + def on_config_changed(self, event): + """Handle changes in configuration""" + unit = self.model.unit + + def on_install(self, event): + """Called when the charm is being installed""" + unit = self.model.unit + + # Install your software and its dependencies + + unit.status = ActiveStatus() + + def on_upgrade_charm(self, event): + """Upgrade the charm.""" + unit = self.model.unit + + # Mark the unit as under Maintenance. + unit.status = MaintenanceStatus("Upgrading charm") + + self.on_install(event) + + # When maintenance is done, return to an Active state + unit.status = ActiveStatus() + + def on_register_action(self, event): + """Register to AGW (EPC).""" + try: + mme_addr = event.params["mme-addr"] + gtp_bind_addr = event.params["gtp-bind-addr"] + s1c_bind_addr = event.params["s1c-bind-addr"] + command = " ".join( + [ + "srsenb", + "--enb.name=dummyENB01", + "--enb.mcc=901", + "--enb.mnc=70", + "--enb.mme_addr={}".format(mme_addr), + "--enb.gtp_bind_addr={}".format(gtp_bind_addr), + "--enb.s1c_bind_addr={}".format(s1c_bind_addr), + "--enb_files.rr_config=/config/rr.conf", + "--enb_files.sib_config=/config/sib.conf", + "--enb_files.drb_config=/config/drb.conf", + "/config/enb.conf.fauxrf", + ] + ) + stdout = subprocess.check_output(command, shell=True) + event.set_results({"output": stdout}) + except subprocess.CalledProcessError as ex: + event.fail(ex) + + def on_attach_ue_action(self, event): + """Attach User Emulator to EnodeB.""" + try: + usim_imsi = event.params["usim-imsi"] + usim_k = event.params["usim-k"] + usim_opc = event.params["usim-opc"] + command = " ".join( + [ + "srsue", + "--usim.imsi={}".format(usim_imsi), + "--usim.k={}".format(usim_k), + "--usim.algo=milenage", + "--usim.opc={}".format(usim_opc), + "--nas.apn=oai.ipv4", + "/config/ue.conf.fauxrf", + ] + ) + stdout = subprocess.check_output(command, shell=True) + event.set_results({"output": stdout}) + except subprocess.CalledProcessError as ex: + event.fail(ex) + + +if __name__ == "__main__": + main(EnodebCharm) diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw deleted file mode 160000 index fbbcbdaacb98be15447b191fd6f0d2110b867ff0..0000000000000000000000000000000000000000 --- a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fbbcbdaacb98be15447b191fd6f0d2110b867ff0 diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/README.md b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0f572b6b57ac95f98bb23bbb901cf4bea089d16f --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/README.md @@ -0,0 +1,3 @@ +# Magma AGW Charm + +This is a proxy charm used by Open Source Mano (OSM) to configure Magma AGW, written in the [Python Operator Framwork](https://github.com/canonical/operator) diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/actions.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/actions.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0750da871491d83a1d948ad69b46abe6e0a19330 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/actions.yaml @@ -0,0 +1,86 @@ +# Magma AGW Actions +add-net: + description: "Add AGW Network if needed" + params: + orch_ip: + description: "Orchestrator IP" + type: "string" + default: "" + orch_net: + description: "Orchestrator Network" + type: "string" + default: "" +add-gw: + description: "Self-register for the AGW" + params: + agw_id: + description: "AGW ID" + type: "string" + default: "" + agw_name: + description: "AGW NAME" + type: "string" + default: "" + orch_ip: + description: "Orchestrator IP" + type: "string" + default: "" + orch_net: + description: "Orchestrator Network" + type: "string" + default: "" +reset-id: + description: "Resets the hardware ID" +add-hosts: + description: "Add Orchestrator host in /etc/hosts" + params: + orch_id: + description: "Orchestrator ID" + type: "string" + default: "" +restart-magma: + description: "Restarts Magma services" +del-gw: + description: "Deregister for AGW" + params: + agw_id: + description: "AGW ID" + type: "string" + default: "" + orch_ip: + description: "Orchestrator IP" + type: "string" + default: "" + orch_net: + description: "Orchestrator Network" + type: "string" + default: "" + +# 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." diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/config.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..56551babcb13b483bff56b55198ae9f8201087b6 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/config.yaml @@ -0,0 +1,29 @@ +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." diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/hooks/install b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/hooks/install new file mode 120000 index 0000000000000000000000000000000000000000..25b1f68fa39d58d33c08ca420c3d439d19be0c55 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/hooks/install @@ -0,0 +1 @@ +../src/charm.py \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/charms/osm b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/charms/osm new file mode 120000 index 0000000000000000000000000000000000000000..385b019dc112a34c6c14d2f77fd25d1d94551ff7 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/charms/osm @@ -0,0 +1 @@ +../../mod/charms.osm/charms/osm \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/ops b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/ops new file mode 120000 index 0000000000000000000000000000000000000000..d93419320c2e0d3133a0bc8059e2f259bf5bb213 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/lib/ops @@ -0,0 +1 @@ +../mod/operator/ops \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/metadata.yaml b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/metadata.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6903089f9e4606591ecaea9db560d19940ede5e4 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/metadata.yaml @@ -0,0 +1,12 @@ +name: magmagw +summary: Charm for Magma AGW +maintainer: David García +description: | + Charm for Magma AGW (version 1.0.0 with extra tools) +series: + - bionic + - xenial +# Needed for proxy charm in HA +peers: + proxypeer: + interface: proxypeer diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/charms.osm b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/charms.osm new file mode 160000 index 0000000000000000000000000000000000000000..cf167cf041a3d4ca9b8dd90e9f575c9c4e99b8ad --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/charms.osm @@ -0,0 +1 @@ +Subproject commit cf167cf041a3d4ca9b8dd90e9f575c9c4e99b8ad diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/operator b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/operator new file mode 160000 index 0000000000000000000000000000000000000000..a3918e1db40edac88a5250c46d7ef3156e8853d3 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/mod/operator @@ -0,0 +1 @@ +Subproject commit a3918e1db40edac88a5250c46d7ef3156e8853d3 diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/charm.py b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/charm.py new file mode 100755 index 0000000000000000000000000000000000000000..1e0ba6aaba4e0e75a15b2b8e9d9da8a4a7023438 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/charm.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python3 +# Copyright 2020 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. + +import sys + +sys.path.append("lib") + +from ops.charm import CharmBase, CharmEvents +from ops.framework import StoredState, EventBase, EventSource +from ops.main import main +from ops.model import ( + ActiveStatus, + BlockedStatus, + MaintenanceStatus, + WaitingStatus, + ModelError, +) +import os +import subprocess +from proxy_cluster import ProxyCluster + +from charms.osm.sshproxy import SSHProxy + + +class SSHKeysInitialized(EventBase): + def __init__(self, handle, ssh_public_key, ssh_private_key): + super().__init__(handle) + self.ssh_public_key = ssh_public_key + self.ssh_private_key = ssh_private_key + + def snapshot(self): + return { + "ssh_public_key": self.ssh_public_key, + "ssh_private_key": self.ssh_private_key, + } + + def restore(self, snapshot): + self.ssh_public_key = snapshot["ssh_public_key"] + self.ssh_private_key = snapshot["ssh_private_key"] + + +class ProxyClusterEvents(CharmEvents): + ssh_keys_initialized = EventSource(SSHKeysInitialized) + + +class SimpleHAProxyCharm(CharmBase): + + state = StoredState() + on = ProxyClusterEvents() + + def __init__(self, framework, key): + super().__init__(framework, key) + + # An example of setting charm state + # that's persistent across events + self.state.set_default(is_started=False) + + self.peers = ProxyCluster(self, "proxypeer") + + if not self.state.is_started: + self.state.is_started = True + + # Register all of the events we want to observe + for event in ( + # Charm events + self.on.config_changed, + self.on.install, + self.on.start, + self.on.upgrade_charm, + # Charm actions (primitives) <-- MAGMA AGW + self.on.add_net_action, + self.on.add_gw_action, + self.on.reset_id_action, + self.on.add_hosts_action, + self.on.restart_magma_action, + self.on.del_gw_action, + self.on.reset_id_action, + # OSM actions (primitives) + self.on.start_action, + self.on.stop_action, + self.on.restart_action, + self.on.reboot_action, + self.on.upgrade_action, + # SSH Proxy actions (primitives) + self.on.generate_ssh_key_action, + self.on.get_ssh_public_key_action, + self.on.run_action, + self.on.verify_ssh_credentials_action, + ): + self.framework.observe(event, self) + + self.framework.observe(self.on.proxypeer_relation_changed, self) + + def get_ssh_proxy(self): + """Get the SSHProxy instance""" + proxy = SSHProxy( + hostname=self.model.config["ssh-hostname"], + username=self.model.config["ssh-username"], + password=self.model.config["ssh-password"], + ) + return proxy + + def on_proxypeer_relation_changed(self, event): + if self.peers.is_cluster_initialized: + pubkey = self.peers.ssh_public_key + privkey = self.peers.ssh_private_key + SSHProxy.write_ssh_keys(public=pubkey, private=privkey) + self.on_config_changed(event) + else: + event.defer() + + def on_config_changed(self, event): + """Handle changes in configuration""" + unit = self.model.unit + + # Unit should go into a waiting state until verify_ssh_credentials is successful + unit.status = WaitingStatus("Waiting for SSH credentials") + proxy = self.get_ssh_proxy() + + verified = proxy.verify_credentials() + if verified: + unit.status = ActiveStatus() + else: + unit.status = BlockedStatus("Invalid SSH credentials.") + + def on_install(self, event): + pass + + def on_start(self, event): + """Called when the charm is being installed""" + if not self.peers.is_joined: + event.defer() + return + + unit = self.model.unit + + if not SSHProxy.has_ssh_key(): + unit.status = MaintenanceStatus("Generating SSH keys...") + pubkey = None + privkey = None + if self.is_leader: + if self.peers.is_cluster_initialized: + SSHProxy.write_ssh_keys( + public=self.peers.ssh_public_key, + private=self.peers.ssh_private_key, + ) + else: + SSHProxy.generate_ssh_key() + self.on.ssh_keys_initialized.emit( + SSHProxy.get_ssh_public_key(), SSHProxy.get_ssh_private_key() + ) + unit.status = ActiveStatus() + else: + unit.status = WaitingStatus("Waiting for leader to populate the keys") + + # Magma AGW Action implementation + def on_add_net_action(self, event): + """Add AGW Network if needed""" + if self.is_leader: + orch_ip = event.params["orch_ip"] + orch_net = event.params["orch_net"] + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run( + "/home/magma/addnet.py --orch_ip {} --orch_net {}".format( + orch_ip, orch_net + ) + ) + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_add_gw_action(self, event): + """Self-register for the AGW""" + if self.is_leader: + agw_id = event.params["agw_id"] + agw_name = event.params["agw_name"] + orch_ip = event.params["orch_ip"] + orch_net = event.params["orch_net"] + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run( + "/home/magma/addgw.py --agw_id {} --agw_name {} --orch_ip {} --orch_net {}".format( + agw_id, agw_name, orch_ip, orch_net + ) + ) + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_reset_id_action(self, event): + """Resets the hardware ID""" + if self.is_leader: + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run("sudo snowflake --force-new-key") + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_add_hosts_action(self, event): + """Add Orchestrator host in /etc/hosts""" + if self.is_leader: + orch_ip = event.params["orch_ip"] + orch_hosts = "ORCH_IP controller.magma.test\nORCH_IP bootstrapper-controller.magma.test\nORCH_IP state-controller.magma.test\nORCH_IP dispatcher-controller.magma.test\nORCH_IP logger-controller.magma.test\nORCH_IP streamer-controller.magma.test\n" + orch_hosts = orch_hosts.replace("ORCH_IP", orch_ip) + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run( + "echo -e {} | sudo tee -a /etc/hosts".format(orch_hosts) + ) + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_restart_magma_action(self, event): + """Resets the hardware ID""" + if self.is_leader: + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run("sudo service magma@* restart") + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_del_gw_action(self, event): + """Deregister from AGW""" + if self.is_leader: + agw_id = event.params["agw_id"] + orch_ip = event.params["orch_ip"] + orch_net = event.params["orch_net"] + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run( + "/home/magma/delgw.py --agw_id {} --orch_ip {} --orch_net {}".format( + agw_id, orch_ip, orch_net + ) + ) + event.set_results({"output": stdout, "stderr": stderr}) + else: + event.fail("Unit is not leader") + return + + def on_upgrade_charm(self, event): + """Upgrade the charm.""" + unit = self.model.unit + + # Mark the unit as under Maintenance. + unit.status = MaintenanceStatus("Upgrading charm") + + self.on_install(event) + + # When maintenance is done, return to an Active state + unit.status = ActiveStatus() + + ############### + # 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.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 + + ##################### + # SSH Proxy methods # + ##################### + def on_generate_ssh_key_action(self, event): + """Generate a new SSH keypair for this unit.""" + if self.is_leader: + if not SSHProxy.generate_ssh_key(): + event.fail("Unable to generate ssh key") + else: + event.fail("Unit is not leader") + return + + def on_get_ssh_public_key_action(self, event): + """Get the SSH public key for this unit.""" + if self.is_leader: + pubkey = SSHProxy.get_ssh_public_key() + event.set_results({"pubkey": SSHProxy.get_ssh_public_key()}) + else: + event.fail("Unit is not leader") + return + + def on_run_action(self, event): + """Run an arbitrary command on the remote host.""" + if self.is_leader: + cmd = event.params["command"] + proxy = self.get_ssh_proxy() + stdout, stderr = proxy.run(cmd) + event.set_results({"output": stdout}) + if len(stderr): + event.fail(stderr) + else: + event.fail("Unit is not leader") + return + + def on_verify_ssh_credentials_action(self, event): + """Verify the SSH credentials for this unit.""" + if self.is_leader: + proxy = self.get_ssh_proxy() + + verified = proxy.verify_credentials() + if verified: + print("Verified!") + event.set_results({"verified": True}) + else: + print("Verification failed!") + event.set_results({"verified": False}) + else: + event.fail("Unit is not leader") + return + + @property + def is_leader(self): + # update the framework to include self.unit.is_leader() + return self.model.unit.is_leader() + + +class LeadershipError(ModelError): + def __init__(self): + super().__init__("not leader") + + +if __name__ == "__main__": + main(SimpleHAProxyCharm) diff --git a/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/proxy_cluster.py b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/proxy_cluster.py new file mode 100644 index 0000000000000000000000000000000000000000..93cd119d4c92271078bf9b85fbe86eca18b415a2 --- /dev/null +++ b/magma/hackfest_magma-agw-enb_vnfd/charms/magmagw/src/proxy_cluster.py @@ -0,0 +1,71 @@ +# Copyright 2020 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. + +from ops.framework import Object, StoredState + + +class ProxyCluster(Object): + + state = StoredState() + + def __init__(self, charm, relation_name): + super().__init__(charm, relation_name) + self._relation_name = relation_name + self._relation = self.framework.model.get_relation(self._relation_name) + + self.framework.observe(charm.on.ssh_keys_initialized, self) + + self.state.set_default(ssh_public_key=None) + self.state.set_default(ssh_private_key=None) + + def on_ssh_keys_initialized(self, event): + if not self.framework.model.unit.is_leader(): + raise RuntimeError("The initial unit of a cluster must also be a leader.") + + self.state.ssh_public_key = event.ssh_public_key + self.state.ssh_private_key = event.ssh_private_key + if not self.is_joined: + event.defer() + return + + self._relation.data[self.model.app][ + "ssh_public_key" + ] = self.state.ssh_public_key + self._relation.data[self.model.app][ + "ssh_private_key" + ] = self.state.ssh_private_key + + @property + def is_joined(self): + return self._relation is not None + + @property + def ssh_public_key(self): + if self.is_joined: + return self._relation.data[self.model.app].get("ssh_public_key") + + @property + def ssh_private_key(self): + if self.is_joined: + return self._relation.data[self.model.app].get("ssh_private_key") + + @property + def is_cluster_initialized(self): + return ( + True + if self.is_joined + and self._relation.data[self.model.app].get("ssh_public_key") + and self._relation.data[self.model.app].get("ssh_private_key") + else False + ) diff --git a/magma/hackfest_magma-agw-enb_vnfd/cloud_init/srslte_init b/magma/hackfest_magma-agw-enb_vnfd/cloud_init/srslte_init index 53aea465b2cdbb4a5ec15a660f53aafa98e387be..cafc9afc5214aee0b0e784e14fac66892cec4f50 100644 --- a/magma/hackfest_magma-agw-enb_vnfd/cloud_init/srslte_init +++ b/magma/hackfest_magma-agw-enb_vnfd/cloud_init/srslte_init @@ -1,8 +1,7 @@ #cloud-config runcmd: - route delete -net 0.0.0.0/0 gw 172.21.251.254 - - route add -net 10.100.0.0/16 gw 172.21.251.254 - - route add -net 10.101.0.0/16 172.21.251.254 + - route add -net 10.0.0.0/8 gw 172.21.251.254 - route add -net 172.21.0.0/16 gw 172.21.251.254 - route add -net 172.22.0.0/16 gw 172.21.251.254 - route add -net 192.168.170.0/24 gw 172.21.251.254 \ No newline at end of file diff --git a/magma/hackfest_magma-agw-enb_vnfd/magma-agw-enb_vnfd.yaml b/magma/hackfest_magma-agw-enb_vnfd/magma-agw-enb_vnfd.yaml index b7b331b72eb26c04fe30937860de699e48506553..e4c32263c768e5c5f74440b6fd0ec514d907f7de 100644 --- a/magma/hackfest_magma-agw-enb_vnfd/magma-agw-enb_vnfd.yaml +++ b/magma/hackfest_magma-agw-enb_vnfd/magma-agw-enb_vnfd.yaml @@ -71,6 +71,7 @@ vnfd:vnfd-catalog: virtual-interface: type: PARAVIRT external-connection-point-ref: srsLTE-mgmt + mgmt-interface: true - name: eth1 type: EXTERNAL virtual-interface: @@ -79,7 +80,32 @@ vnfd:vnfd-catalog: internal-connection-point: - id: srsLTE-s1 name: srsLTE-s1 - short-name: srsLTE-s1 + short-name: srsLTE-s1 + vdu-configuration: + juju: + charm: enodeb + proxy: False + config-access: + ssh-access: + required: True + default-user: ubuntu + config-primitive: + - name: register + parameter: + - name: mme-addr + data-type: STRING + - name: gtp-bind-addr + data-type: STRING + - name: s1c-bind-addr + data-type: STRING + - name: attach-ue + parameter: + - name: usim-imsi + data-type: STRING + - name: usim-k + data-type: STRING + - name: usim-opc + data-type: STRING internal-vld: - id: internalS1 name: internalS1