Added microstack.remove command
Running microstack.remove will remove the br-ex virtual bridge device, then uninstall MicroStack. We do this because we can't use ovs-ctl to remove the bridge as part of a remove hook, as the Open vSwitch daemons are not running at that point. The microstack.remove command gives operators a way to cleanly uninstall the snap, without needing to reboot to get rid of br-ex. Added test exercising the code to test_basic.py. Rerranged entry points a bit (moved some things into main.py) to make code sharing easier, and to prevent a proliferation of entry point scripts in our root dir. Change-Id: I9ff25864cd96ada3a9b3da8992c2b33955eff0b4 Closes-Bug: #1852147
This commit is contained in:
parent
1a25e50a17
commit
a89f5574c3
17
README.md
17
README.md
@ -67,6 +67,23 @@ credentials are:
|
|||||||
username: admin
|
username: admin
|
||||||
password: keystone
|
password: keystone
|
||||||
|
|
||||||
|
## Removing MicroStack
|
||||||
|
|
||||||
|
To remove MicroStack, run:
|
||||||
|
|
||||||
|
sudo microstack.remove --auto
|
||||||
|
|
||||||
|
This will clean up the Open vSwitch bridge device and uninstall
|
||||||
|
MicroStack. If you remove MicroStack with the `snap remove` command
|
||||||
|
instead, don't worry -- the Open vSwitch bridge will disappear the
|
||||||
|
next time that you reboot your system.
|
||||||
|
|
||||||
|
Note that you can pass any arguments that you'd pass to the `snap
|
||||||
|
remove` command to `microstack.remove`. To purge the snap,
|
||||||
|
for example, run:
|
||||||
|
|
||||||
|
sudo microstack.remove --auto --purge
|
||||||
|
|
||||||
## Customising and contributing
|
## Customising and contributing
|
||||||
|
|
||||||
To customise services and settings, look in the `.d` directories under
|
To customise services and settings, look in the `.d` directories under
|
||||||
|
@ -52,3 +52,9 @@ snapctl set \
|
|||||||
cluster.role=control \
|
cluster.role=control \
|
||||||
cluster.password=null \
|
cluster.password=null \
|
||||||
;
|
;
|
||||||
|
|
||||||
|
# Uninstall stuff
|
||||||
|
snapctl set \
|
||||||
|
config.cleanup.delete-bridge=true \
|
||||||
|
config.cleanup.remove=true \
|
||||||
|
;
|
||||||
|
@ -27,6 +27,9 @@ apps:
|
|||||||
# plugs:
|
# plugs:
|
||||||
# - network
|
# - network
|
||||||
|
|
||||||
|
remove:
|
||||||
|
command: microstack_remove
|
||||||
|
|
||||||
# Keystone
|
# Keystone
|
||||||
keystone-uwsgi:
|
keystone-uwsgi:
|
||||||
command: snap-openstack launch keystone-uwsgi
|
command: snap-openstack launch keystone-uwsgi
|
||||||
|
@ -141,6 +141,8 @@ class Host():
|
|||||||
check('sudo', 'multipass', 'delete', self.machine)
|
check('sudo', 'multipass', 'delete', self.machine)
|
||||||
check('sudo', 'multipass', 'purge')
|
check('sudo', 'multipass', 'purge')
|
||||||
else:
|
else:
|
||||||
|
if call('snap', 'list', 'microstack'):
|
||||||
|
# Uninstall microstack if it is installed (it may not be).
|
||||||
check('sudo', 'snap', 'remove', '--purge', 'microstack')
|
check('sudo', 'snap', 'remove', '--purge', 'microstack')
|
||||||
|
|
||||||
|
|
||||||
@ -241,7 +243,7 @@ class Framework(unittest.TestCase):
|
|||||||
self.driver.find_element(By.LINK_TEXT, "Images").click()
|
self.driver.find_element(By.LINK_TEXT, "Images").click()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.passed = False # HACK: trigger (or skip) cleanup.
|
self.passed = False # HACK: trigger (or skip) log dumps.
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Clean hosts up, possibly leaving debug information behind."""
|
"""Clean hosts up, possibly leaving debug information behind."""
|
||||||
|
@ -65,6 +65,27 @@ class TestBasics(Framework):
|
|||||||
# The Horizon Dashboard should function
|
# The Horizon Dashboard should function
|
||||||
self.verify_gui(host)
|
self.verify_gui(host)
|
||||||
|
|
||||||
|
# Verify that we can uninstall the snap cleanly, and that the
|
||||||
|
# ovs bridge goes away.
|
||||||
|
|
||||||
|
# Check to verify that our bridge is there.
|
||||||
|
self.assertTrue('br-ex' in check_output(*prefix, 'ip', 'a'))
|
||||||
|
|
||||||
|
# Try to uninstall snap without sudo.
|
||||||
|
self.assertFalse(call(*prefix, '/snap/bin/microstack.remove',
|
||||||
|
'--purge', '--auto'))
|
||||||
|
|
||||||
|
# Retry with sudo (should succeed).
|
||||||
|
check(*prefix, 'sudo', '/snap/bin/microstack.remove',
|
||||||
|
'--purge', '--auto')
|
||||||
|
|
||||||
|
# Verify that MicroStack is gone.
|
||||||
|
self.assertFalse(call(*prefix, 'snap', 'list', 'microstack'))
|
||||||
|
|
||||||
|
# Verify that bridge is gone.
|
||||||
|
self.assertFalse('br-ex' in check_output(*prefix, 'ip', 'a'))
|
||||||
|
|
||||||
|
# We made it to the end. Set passed to True!
|
||||||
self.passed = True
|
self.passed = True
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,13 +35,27 @@ import secrets
|
|||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
from init.config import log
|
from init.config import log
|
||||||
from init.shell import check
|
from init.shell import default_network, check, check_output
|
||||||
|
|
||||||
from init import questions
|
from init import questions
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def requires_sudo(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if int(check_output('id', '-u')):
|
||||||
|
log.error("This script must be run with root privileges. "
|
||||||
|
"Please re-run with sudo.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def parse_init_args():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--auto', '-a', action='store_true',
|
parser.add_argument('--auto', '-a', action='store_true',
|
||||||
help='Run non interactively.')
|
help='Run non interactively.')
|
||||||
@ -53,7 +67,7 @@ def parse_args():
|
|||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def process_args(args):
|
def process_init_args(args):
|
||||||
"""Look through our args object and set the proper default config
|
"""Look through our args object and set the proper default config
|
||||||
values in our snap config, based on those args.
|
values in our snap config, based on those args.
|
||||||
|
|
||||||
@ -80,7 +94,8 @@ def process_args(args):
|
|||||||
if auto and not args.cluster_password:
|
if auto and not args.cluster_password:
|
||||||
alphabet = string.ascii_letters + string.digits
|
alphabet = string.ascii_letters + string.digits
|
||||||
password = ''.join(secrets.choice(alphabet) for i in range(10))
|
password = ''.join(secrets.choice(alphabet) for i in range(10))
|
||||||
check('snapctl', 'set', 'config.cluster.password={}'.format(password))
|
check('snapctl', 'set', 'config.cluster.password={}'.format(
|
||||||
|
password))
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
log.setLevel(logging.DEBUG)
|
log.setLevel(logging.DEBUG)
|
||||||
@ -88,9 +103,10 @@ def process_args(args):
|
|||||||
return auto
|
return auto
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
@requires_sudo
|
||||||
args = parse_args()
|
def init() -> None:
|
||||||
auto = process_args(args)
|
args = parse_init_args()
|
||||||
|
auto = process_init_args(args)
|
||||||
|
|
||||||
question_list = [
|
question_list = [
|
||||||
questions.Clustering(),
|
questions.Clustering(),
|
||||||
@ -125,5 +141,47 @@ def main() -> None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def set_network_info() -> None:
|
||||||
main()
|
"""Find and use the default network on a machine.
|
||||||
|
|
||||||
|
Helper to find the default network on a machine, and configure
|
||||||
|
MicroStack to use it in its default settings.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ip, gate, cidr = default_network()
|
||||||
|
except Exception:
|
||||||
|
# TODO: more specific exception handling.
|
||||||
|
log.exception(
|
||||||
|
'Could not determine default network info. '
|
||||||
|
'Falling back on 10.20.20.1')
|
||||||
|
return
|
||||||
|
|
||||||
|
check('snapctl', 'set', 'config.network.ext-gateway={}'.format(gate))
|
||||||
|
check('snapctl', 'set', 'config.network.ext-cidr={}'.format(cidr))
|
||||||
|
check('snapctl', 'set', 'config.network.control-ip={}'.format(ip))
|
||||||
|
check('snapctl', 'set', 'config.network.control-ip={}'.format(ip))
|
||||||
|
|
||||||
|
|
||||||
|
@requires_sudo
|
||||||
|
def remove() -> None:
|
||||||
|
"""Helper to cleanly uninstall MicroStack."""
|
||||||
|
|
||||||
|
# Strip '--auto' out of the args passed to this command, as we
|
||||||
|
# need to check it, but also pass the other args off to the
|
||||||
|
# snapd's uninstall command. TODO: make this less hacky.
|
||||||
|
auto = False
|
||||||
|
if '--auto' in questions.uninstall.ARGS:
|
||||||
|
auto = True
|
||||||
|
questions.uninstall.ARGS = [
|
||||||
|
arg for arg in questions.uninstall.ARGS if 'auto' not in arg]
|
||||||
|
|
||||||
|
question_list = [
|
||||||
|
questions.uninstall.DeleteBridge(),
|
||||||
|
questions.uninstall.RemoveMicrostack(),
|
||||||
|
]
|
||||||
|
|
||||||
|
for question in question_list:
|
||||||
|
if auto:
|
||||||
|
question.interactive = False
|
||||||
|
question.ask()
|
||||||
|
@ -31,7 +31,7 @@ from init.shell import (check, call, check_output, shell, sql, nc_wait,
|
|||||||
log_wait, restart, download)
|
log_wait, restart, download)
|
||||||
from init.config import Env, log
|
from init.config import Env, log
|
||||||
from init.questions.question import Question
|
from init.questions.question import Question
|
||||||
from init.questions import clustering, network
|
from init.questions import clustering, network, uninstall # noqa F401
|
||||||
|
|
||||||
|
|
||||||
_env = Env().get_env()
|
_env = Env().get_env()
|
||||||
|
47
tools/init/init/questions/uninstall.py
Normal file
47
tools/init/init/questions/uninstall.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from init.config import Env, log
|
||||||
|
from init.questions.question import Question
|
||||||
|
from init.shell import check, call
|
||||||
|
|
||||||
|
_env = Env().get_env()
|
||||||
|
|
||||||
|
|
||||||
|
# Save off command line args. If you use any of these to answer a
|
||||||
|
# question, pop it from this list -- the remaining args will get
|
||||||
|
# passed to "snap remove"
|
||||||
|
ARGS = list(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteBridge(Question):
|
||||||
|
_type = 'boolean'
|
||||||
|
_question = 'Do you wish to delete the ovs bridge? (br-ex)'
|
||||||
|
interactive = True
|
||||||
|
config_key = 'config.cleanup.delete-bridge'
|
||||||
|
|
||||||
|
def yes(self, answer):
|
||||||
|
log.info('Removing ovs bridge.')
|
||||||
|
# Remove bridge. This may not exist, so we silently skip on error.
|
||||||
|
# TODO get bridge name from config (if it gets added to config)
|
||||||
|
# TODO clean up other ovs artifacts?
|
||||||
|
call('ovs-vsctl', 'del-br', 'br-ex')
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: cleanup system optimizations
|
||||||
|
# TODO: cleanup kernel modules?
|
||||||
|
# TODO: cleanup iptables rules
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveMicrostack(Question):
|
||||||
|
_type = 'auto'
|
||||||
|
_question = 'Do you really wish to remove MicroStack?'
|
||||||
|
interactive = True
|
||||||
|
config_key = 'config.cleanup.remove'
|
||||||
|
|
||||||
|
def yes(self, answer):
|
||||||
|
"""Uninstall MicroStack, passing any command line options to snapd."""
|
||||||
|
|
||||||
|
log.info('Uninstalling MicroStack (this may take a while) ...')
|
||||||
|
check('snap', 'remove', '{SNAP_INSTANCE_NAME}'.format(**_env),
|
||||||
|
*ARGS)
|
||||||
|
log.info('MicroStack has been removed from your system!')
|
@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env/python3
|
|
||||||
from init.shell import default_network, check
|
|
||||||
|
|
||||||
from init.config import log # TODO name log.
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
try:
|
|
||||||
ip, gate, cidr = default_network()
|
|
||||||
except Exception:
|
|
||||||
# TODO: more specific exception handling.
|
|
||||||
log.exception(
|
|
||||||
'Could not determine default network info. '
|
|
||||||
'Falling back on 10.20.20.1')
|
|
||||||
return
|
|
||||||
|
|
||||||
check('snapctl', 'set', 'config.network.ext-gateway={}'.format(gate))
|
|
||||||
check('snapctl', 'set', 'config.network.ext-cidr={}'.format(cidr))
|
|
||||||
check('snapctl', 'set', 'config.network.control-ip={}'.format(ip))
|
|
||||||
check('snapctl', 'set', 'config.network.control-ip={}'.format(ip))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -7,8 +7,9 @@ setup(
|
|||||||
version="0.0.1",
|
version="0.0.1",
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'microstack_init = init.main:main',
|
'microstack_init = init.main:init',
|
||||||
'set_network_info = init.set_network_info:main',
|
'set_network_info = init.main:set_network_info',
|
||||||
|
'microstack_remove = init.main:remove',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user