You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PurpleDome/plugins/default/vm_controller/vagrant/vagrant_plugin.py

120 lines
4.2 KiB
Python

#!/usr/bin/env python3
# A plugin to control vagrant machines
from plugins.base.machinery import MachineryPlugin, MachineStates
import subprocess
import vagrant
from fabric import Connection
import os
from app.exceptions import ConfigurationError
# from app.exceptions import NetworkError
# from invoke.exceptions import UnexpectedExit
# import paramiko
from plugins.base.ssh_features import SSHFeatures
from typing import Any
# Experiment with paramiko instead of fabric. Seems fabric has some issues with the "put" command to Windows. There seems no fix (just my workarounds). Maybe paramiko is better.
class VagrantPlugin(SSHFeatures, MachineryPlugin):
# Boilerplate
name = "vagrant"
description = "A plugin for vagrant machines"
required_files = [] # Files shipped with the plugin which are needed by the machine. Will be copied to the share
def __init__(self):
super().__init__()
self.plugin_path = __file__
self.v = None
self.connection = None
self.vagrantfilepath = None
self.vagrantfile = None
# self.sysconf = {}
def process_config(self, config):
""" Machine specific processing of configuration """
super().process_config(config)
self.vagrantfilepath = os.path.abspath(self.config.vagrantfilepath())
self.vagrantfile = os.path.join(self.vagrantfilepath, "Vagrantfile")
if not os.path.isfile(self.vagrantfile):
raise ConfigurationError(f"Vagrantfile not existing: {self.vagrantfile}")
self.v = vagrant.Vagrant(root=self.vagrantfilepath)
def create(self, reboot=True):
""" Create a machine
@param reboot: Reboot the VM during installation. Required if you want to install software
"""
self.up()
if reboot:
self.halt()
self.up()
def up(self):
""" Start a machine, create it if it does not exist """
try:
self.v.up(vm_name=self.config.vmname())
except subprocess.CalledProcessError as e:
if self.v.status(vm_name=self.config.vmname())[0].state == self.v.RUNNING:
return # Everything is fine
else:
raise e
def halt(self):
""" Halt a machine """
forceit = self.config.halt_needs_force()
try:
self.v.halt(vm_name=self.config.vmname(), force=forceit)
except subprocess.CalledProcessError:
self.v.halt(vm_name=self.config.vmname(), force=True)
def destroy(self):
""" Destroy a machine """
self.v.destroy(vm_name=self.config.vmname())
def connect(self) -> Any:
""" Connect to a machine. If there is already a connection we keep it """
# For linux we are using Vagrant style
if self.config.os() == "linux":
if self.connection:
return self.connection
uhp = self.v.user_hostname_port(vm_name=self.config.vmname())
self.vprint(f"Vagrant connecting to {uhp} ({self.get_vm_name()}/{self.config.vmname()})", 3)
self.connection = Connection(uhp, connect_kwargs={"key_filename": self.v.keyfile(vm_name=self.config.vmname())})
self.vprint(f"Vagrant connection: {self.connection.is_connected}", 3)
return self.connection
else:
return super().connect()
def get_state(self):
""" Get detailed state of a machine """
vstate = self.v.status(vm_name=self.config.vmname())[0].state
# mapping vagrant states to PurpleDome states
mapping = {self.v.RUNNING: MachineStates.RUNNING,
self.v.NOT_CREATED: MachineStates.NOT_CREATED,
self.v.POWEROFF: MachineStates.POWEROFF,
self.v.ABORTED: MachineStates.ABORTED,
self.v.SAVED: MachineStates.SAVED,
self.v.STOPPED: MachineStates.STOPPED,
self.v.FROZEN: MachineStates.FROZEN,
self.v.SHUTOFF: MachineStates.SHUTOFF
}
return mapping[vstate]
def get_ip(self) -> str:
""" Return the machine ip """
filename = os.path.join(self.get_machine_path_external(), "ip4.txt")
with open(filename, "rt") as fh:
return fh.readline().strip()