From b7455ffdf4eb3c999e86d3da5313eaf41a9586a5 Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Fri, 19 Feb 2016 14:41:37 +0000 Subject: [PATCH] Add support for SASL authentication with libvirt --- virtualbmc/cmd/vbmc.py | 26 ++++++++++++++++++++++++-- virtualbmc/manager.py | 37 +++++++++++++++++++++++++++++-------- virtualbmc/utils.py | 27 +++++++++++++++++++++++---- virtualbmc/virtualbmc.py | 17 ++++++++++------- 4 files changed, 86 insertions(+), 21 deletions(-) diff --git a/virtualbmc/cmd/vbmc.py b/virtualbmc/cmd/vbmc.py index 13720be..56800a3 100644 --- a/virtualbmc/cmd/vbmc.py +++ b/virtualbmc/cmd/vbmc.py @@ -57,6 +57,16 @@ def main(): default="qemu:///system", help=('The libvirt URI; defaults to ' '"qemu:///system"')) + parser_add.add_argument('--libvirt-sasl-username', + dest='libvirt_sasl_username', + default=None, + help=('The libvirt SASL username; defaults to ' + 'None')) + parser_add.add_argument('--libvirt-sasl-password', + dest='libvirt_sasl_password', + default=None, + help=('The libvirt SASL password; defaults to ' + 'None')) # create the parser for the "delete" command parser_delete = subparsers.add_parser('delete', @@ -92,10 +102,22 @@ def main(): try: if args.command == 'add': + + # Check if the username and password were given for SASL + sasl_user = args.libvirt_sasl_username + sasl_pass = args.libvirt_sasl_password + if any((sasl_user, sasl_pass)): + if not all((sasl_user, sasl_pass)): + print("A password and username are required to use " + "Libvirt's SASL authentication", file=sys.stderr) + exit(1) + manager.add(username=args.username, password=args.password, port=args.port, address=args.address, domain_name=args.domain_name, - libvirt_uri=args.libvirt_uri) + libvirt_uri=args.libvirt_uri, + libvirt_sasl_username=sasl_user, + libvirt_sasl_password=sasl_pass) elif args.command == 'delete': for domain in args.domain_names: @@ -118,7 +140,7 @@ def main(): elif args.command == 'show': ptable = PrettyTable(['Property', 'Value']) bmc = manager.show(args.domain_name) - for key, val in bmc.items(): + for key, val in sorted(bmc.items()): ptable.add_row([key, val]) print(ptable) diff --git a/virtualbmc/manager.py b/virtualbmc/manager.py index 9b107c0..23c9da1 100644 --- a/virtualbmc/manager.py +++ b/virtualbmc/manager.py @@ -44,12 +44,18 @@ class VirtualBMCManager(object): config.read(config_path) bmc = {} - for item in ('username', 'password', 'address', - 'domain_name', 'libvirt_uri'): - bmc[item] = config.get(DEFAULT_SECTION, item) + for item in ('username', 'password', 'address', 'domain_name', + 'libvirt_uri', 'libvirt_sasl_username', + 'libvirt_sasl_password'): + try: + value = config.get(DEFAULT_SECTION, item) + except configparser.NoOptionError: + value = None + + bmc[item] = value # Port needs to be int - bmc['port'] = int(config.get(DEFAULT_SECTION, 'port')) + bmc['port'] = config.getint(DEFAULT_SECTION, 'port') return bmc @@ -68,9 +74,14 @@ class VirtualBMCManager(object): bmc_config['status'] = RUNNING if running else DOWN return bmc_config - def add(self, username, password, port, address, - domain_name, libvirt_uri): - utils.check_libvirt_connection_and_domain(libvirt_uri, domain_name) + def add(self, username, password, port, address, domain_name, libvirt_uri, + libvirt_sasl_username, libvirt_sasl_password): + + # check libvirt's connection and if domain exist prior to adding it + utils.check_libvirt_connection_and_domain( + libvirt_uri, domain_name, + sasl_username=libvirt_sasl_username, + sasl_password=libvirt_sasl_password) domain_path = os.path.join(utils.CONFIG_PATH, domain_name) try: @@ -89,6 +100,13 @@ class VirtualBMCManager(object): config.set(DEFAULT_SECTION, 'address', address) config.set(DEFAULT_SECTION, 'domain_name', domain_name) config.set(DEFAULT_SECTION, 'libvirt_uri', libvirt_uri) + + if libvirt_sasl_username and libvirt_sasl_password: + config.set(DEFAULT_SECTION, 'libvirt_sasl_username', + libvirt_sasl_username) + config.set(DEFAULT_SECTION, 'libvirt_sasl_password', + libvirt_sasl_password) + config.write(f) def delete(self, domain_name): @@ -110,8 +128,11 @@ class VirtualBMCManager(object): bmc_config = self._parse_config(domain_name) + # check libvirt's connection and domain prior to starting the BMC utils.check_libvirt_connection_and_domain( - bmc_config['libvirt_uri'], domain_name) + bmc_config['libvirt_uri'], domain_name, + sasl_username=bmc_config['libvirt_sasl_username'], + sasl_password=bmc_config['libvirt_sasl_password']) LOG.debug('Starting a Virtual BMC for domain %(domain)s with the ' 'following configuration options: %(config)s', diff --git a/virtualbmc/utils.py b/virtualbmc/utils.py index 2c03dff..363c0f6 100644 --- a/virtualbmc/utils.py +++ b/virtualbmc/utils.py @@ -20,13 +20,30 @@ CONFIG_PATH = os.path.join(os.path.expanduser('~'), '.vbmc') class libvirt_open(object): - def __init__(self, uri, readonly=False): + def __init__(self, uri, sasl_username=None, sasl_password=None, + readonly=False): self.uri = uri + self.sasl_username = sasl_username + self.sasl_password = sasl_password self.readonly = readonly def __enter__(self): try: - if self.readonly: + if self.sasl_username and self.sasl_password: + + def request_cred(credentials, user_data): + for credential in credentials: + if credential[0] == libvirt.VIR_CRED_AUTHNAME: + credential[4] = self.sasl_username + elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: + credential[4] = self.sasl_password + return 0 + + auth = [[libvirt.VIR_CRED_AUTHNAME, + libvirt.VIR_CRED_PASSPHRASE], request_cred, None] + flags = libvirt.VIR_CONNECT_RO if self.readonly else 0 + self.conn = libvirt.openAuth(self.uri, auth, flags) + elif self.readonly: self.conn = libvirt.openReadOnly(self.uri) else: self.conn = libvirt.open(self.uri) @@ -47,8 +64,10 @@ def get_libvirt_domain(conn, domain): raise exception.DomainNotFound(domain=domain) -def check_libvirt_connection_and_domain(uri, domain): - with libvirt_open(uri, readonly=True) as conn: +def check_libvirt_connection_and_domain(uri, domain, sasl_username=None, + sasl_password=None): + with libvirt_open(uri, readonly=True, sasl_username=sasl_username, + sasl_password=sasl_password) as conn: get_libvirt_domain(conn, domain) diff --git a/virtualbmc/virtualbmc.py b/virtualbmc/virtualbmc.py index 69a32d4..03486c5 100644 --- a/virtualbmc/virtualbmc.py +++ b/virtualbmc/virtualbmc.py @@ -43,15 +43,18 @@ SET_BOOT_DEVICES_MAP = { class VirtualBMC(bmc.Bmc): def __init__(self, username, password, port, address, - domain_name, libvirt_uri): + domain_name, libvirt_uri, libvirt_sasl_username=None, + libvirt_sasl_password=None): super(VirtualBMC, self).__init__({username: password}, port=port, address=address) - self.libvirt_uri = libvirt_uri self.domain_name = domain_name + self._conn_args = {'uri': libvirt_uri, + 'sasl_username': libvirt_sasl_username, + 'sasl_password': libvirt_sasl_password} def get_boot_device(self): LOG.debug('Get boot device called for %s', self.domain_name) - with utils.libvirt_open(self.libvirt_uri, readonly=True) as conn: + with utils.libvirt_open(readonly=True, **self._conn_args) as conn: domain = utils.get_libvirt_domain(conn, self.domain_name) boot_element = ET.fromstring(domain.XMLDesc()).find('.//os/boot') boot_dev = None @@ -67,7 +70,7 @@ class VirtualBMC(bmc.Bmc): if device is None: return 0xd5 - with utils.libvirt_open(self.libvirt_uri) as conn: + with utils.libvirt_open(**self._conn_args) as conn: domain = utils.get_libvirt_domain(conn, self.domain_name) tree = ET.fromstring(domain.XMLDesc()) @@ -90,7 +93,7 @@ class VirtualBMC(bmc.Bmc): def get_power_state(self): LOG.debug('Get power state called for domain %s', self.domain_name) try: - with utils.libvirt_open(self.libvirt_uri, readonly=True) as conn: + with utils.libvirt_open(readonly=True, **self._conn_args) as conn: domain = utils.get_libvirt_domain(conn, self.domain_name) if domain.isActive(): return POWERON @@ -105,7 +108,7 @@ class VirtualBMC(bmc.Bmc): def power_off(self): LOG.debug('Power off called for domain %s', self.domain_name) try: - with utils.libvirt_open(self.libvirt_uri) as conn: + with utils.libvirt_open(**self._conn_args) as conn: domain = utils.get_libvirt_domain(conn, self.domain_name) if domain.isActive(): domain.destroy() @@ -118,7 +121,7 @@ class VirtualBMC(bmc.Bmc): def power_on(self): LOG.debug('Power on called for domain %s', self.domain_name) try: - with utils.libvirt_open(self.libvirt_uri) as conn: + with utils.libvirt_open(**self._conn_args) as conn: domain = utils.get_libvirt_domain(conn, self.domain_name) if not domain.isActive(): domain.create()