Commit 84f49688 authored by lavado's avatar lavado
Browse files

Merge branch 'improve-vyos-fix-magmagw' into 'master'

Improve vyos-charm and fix some edge cases in magmagw-charm

See merge request !87
parents 4f652622 092fe2ec
......@@ -9,18 +9,6 @@ configure-remote:
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"
......
......@@ -30,145 +30,48 @@ from ops.model import (
import os
import subprocess
import traceback
from proxy_cluster import ProxyCluster
from charms.osm.sshproxy import SSHProxy
from charms.osm.sshproxy import SSHProxyCharm
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()
class VyosCharm(SSHProxyCharm):
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
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.configure_remote_action, self.on_configure_remote_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)
# SSH Proxy actions (primitives)
self.framework.observe(self.on.generate_ssh_key_action, self.on_generate_ssh_key_action)
self.framework.observe(self.on.get_ssh_public_key_action, self.on_get_ssh_public_key_action)
self.framework.observe(self.on.run_action, self.on_run_action)
self.framework.observe(self.on.verify_ssh_credentials_action, self.on_verify_ssh_credentials_action)
self.framework.observe(self.on.proxypeer_relation_changed, self.on_proxypeer_relation_changed)
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"],
self.framework.observe(
self.on.configure_remote_action, self.on_configure_remote_action
)
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.")
super().on_config_changed(event)
def on_install(self, event):
unit = self.model.unit
unit.status = MaintenanceStatus("Installing Ansible")
"""Called when the charm is being installed"""
super().on_install(event)
self.unit.status = MaintenanceStatus("Installing Ansible")
libansible.install_ansible_support()
unit.status = ActiveStatus()
self.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")
"""Called when the charm is being started"""
super().on_start(event)
def on_configure_remote_action(self, event):
"""Configure remote."""
if self.is_leader:
if self.unit.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"],
......@@ -188,106 +91,7 @@ class SimpleHAProxyCharm(CharmBase):
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)
main(VyosCharm)
# 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.on_ssh_keys_initialized)
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
)
......@@ -67,18 +67,6 @@ add-test-subscriber:
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"
......
......@@ -146,7 +146,16 @@ class MagmaAGWProxyCharm(SSHProxyCharm):
"""Resets the hardware ID"""
if self.unit.is_leader():
proxy = self.get_ssh_proxy()
stdout, stderr = proxy.run("sudo snowflake --force-new-key")
attempt = 0
while attempt < 50:
try:
stdout, stderr = proxy.run("sudo snowflake --force-new-key")
break
except subprocess.CalledProcessError:
attempt += 1
import time
time.sleep(5)
event.set_results({"output": stdout, "stderr": stderr})
else:
event.fail("Unit is not leader")
......
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