synced charm-helpers

This commit is contained in:
Edward Hope-Morley 2014-09-26 13:40:58 +01:00
parent a2b51aba7a
commit fc598df4a3
5 changed files with 77 additions and 29 deletions

View File

@ -1,4 +1,6 @@
import glob import glob
import re
import subprocess
import sys import sys
from functools import partial from functools import partial
@ -172,7 +174,8 @@ def format_ipv6_addr(address):
return address return address
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=True, exc_list=None): def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
fatal=True, exc_list=None):
""" """
Return the assigned IP address for a given interface, if any, or []. Return the assigned IP address for a given interface, if any, or [].
""" """
@ -212,26 +215,67 @@ def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=T
if 'addr' in entry and entry['addr'] not in exc_list: if 'addr' in entry and entry['addr'] not in exc_list:
addresses.append(entry['addr']) addresses.append(entry['addr'])
if fatal and not addresses: if fatal and not addresses:
raise Exception("Interface '%s' doesn't have any %s addresses." % (iface, inet_type)) raise Exception("Interface '%s' doesn't have any %s addresses." %
(iface, inet_type))
return addresses return addresses
get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET') get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET')
def get_ipv6_addr(iface='eth0', inc_aliases=False, fatal=True, exc_list=None): def get_ipv6_addr(iface='eth0', inc_aliases=False, fatal=True, exc_list=None,
""" dynamic_only=True):
Return the assigned IPv6 address for a given interface, if any, or []. """Get assigned IPv6 address for a given interface.
Returns list of addresses found. If no address found, returns empty list.
We currently only support scope global IPv6 addresses i.e. non-temporary
addresses. If no global IPv6 address is found, return the first one found
in the ipv6 address list.
""" """
addresses = get_iface_addr(iface=iface, inet_type='AF_INET6', addresses = get_iface_addr(iface=iface, inet_type='AF_INET6',
inc_aliases=inc_aliases, fatal=fatal, inc_aliases=inc_aliases, fatal=fatal,
exc_list=exc_list) exc_list=exc_list)
remotly_addressable = []
for address in addresses: if addresses:
if not address.startswith('fe80'): global_addrs = []
remotly_addressable.append(address) for addr in addresses:
if fatal and not remotly_addressable: key_scope_link_local = re.compile("^fe80::..(.+)%(.+)")
raise Exception("Interface '%s' doesn't have global ipv6 address." % iface) m = re.match(key_scope_link_local, addr)
return remotly_addressable if m:
eui_64_mac = m.group(1)
iface = m.group(2)
else:
global_addrs.append(addr)
if global_addrs:
# Make sure any found global addresses are not temporary
cmd = ['ip', 'addr', 'show', iface]
out = subprocess.check_output(cmd)
if dynamic_only:
key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")
else:
key = re.compile("inet6 (.+)/[0-9]+ scope global.*")
addrs = []
for line in out.split('\n'):
line = line.strip()
m = re.match(key, line)
if m and 'temporary' not in line:
# Return the first valid address we find
for addr in global_addrs:
if m.group(1) == addr:
if not dynamic_only or \
m.group(1).endswith(eui_64_mac):
addrs.append(addr)
if addrs:
return addrs
if fatal:
raise Exception("Interface '%s' doesn't have a scope global "
"non-temporary ipv6 address." % iface)
return []
def get_bridges(vnic_dir='/sys/devices/virtual/net'): def get_bridges(vnic_dir='/sys/devices/virtual/net'):

View File

@ -486,9 +486,10 @@ class Hooks(object):
hooks.execute(sys.argv) hooks.execute(sys.argv)
""" """
def __init__(self): def __init__(self, config_save=True):
super(Hooks, self).__init__() super(Hooks, self).__init__()
self._hooks = {} self._hooks = {}
self._config_save = config_save
def register(self, name, function): def register(self, name, function):
"""Register a hook""" """Register a hook"""
@ -499,9 +500,10 @@ class Hooks(object):
hook_name = os.path.basename(args[0]) hook_name = os.path.basename(args[0])
if hook_name in self._hooks: if hook_name in self._hooks:
self._hooks[hook_name]() self._hooks[hook_name]()
cfg = config() if self._config_save:
if cfg.implicit_save: cfg = config()
cfg.save() if cfg.implicit_save:
cfg.save()
else: else:
raise UnregisteredHookError(hook_name) raise UnregisteredHookError(hook_name)

View File

@ -68,8 +68,8 @@ def service_available(service_name):
"""Determine whether a system service is available""" """Determine whether a system service is available"""
try: try:
subprocess.check_output(['service', service_name, 'status'], stderr=subprocess.STDOUT) subprocess.check_output(['service', service_name, 'status'], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError: except subprocess.CalledProcessError as e:
return False return 'unrecognized service' not in e.output
else: else:
return True return True
@ -229,12 +229,12 @@ def check_hash(path, checksum, hash_type='md5'):
""" """
Validate a file using a cryptographic checksum. Validate a file using a cryptographic checksum.
:param str checksum: Value of the checksum used to validate the file. :param str checksum: Value of the checksum used to validate the file.
:param str hash_type: Hash algorithm used to generate :param:`checksum`. :param str hash_type: Hash algorithm used to generate `checksum`.
Can be any hash alrgorithm supported by :mod:`hashlib`, Can be any hash alrgorithm supported by :mod:`hashlib`,
such as md5, sha1, sha256, sha512, etc. such as md5, sha1, sha256, sha512, etc.
:raises ChecksumError: If the file fails the checksum :raises ChecksumError: If the file fails the checksum
""" """
actual_checksum = file_hash(path, hash_type) actual_checksum = file_hash(path, hash_type)
if checksum != actual_checksum: if checksum != actual_checksum:

View File

@ -208,7 +208,8 @@ def add_source(source, key=None):
"""Add a package source to this system. """Add a package source to this system.
@param source: a URL or sources.list entry, as supported by @param source: a URL or sources.list entry, as supported by
add-apt-repository(1). Examples: add-apt-repository(1). Examples::
ppa:charmers/example ppa:charmers/example
deb https://stub:key@private.example.com/ubuntu trusty main deb https://stub:key@private.example.com/ubuntu trusty main

View File

@ -74,18 +74,19 @@ class ArchiveUrlFetchHandler(BaseFetchHandler):
""" """
Download and install an archive file, with optional checksum validation. Download and install an archive file, with optional checksum validation.
The checksum can also be given on the :param:`source` URL's fragment. The checksum can also be given on the `source` URL's fragment.
For example:: For example::
handler.install('http://example.com/file.tgz#sha1=deadbeef') handler.install('http://example.com/file.tgz#sha1=deadbeef')
:param str source: URL pointing to an archive file. :param str source: URL pointing to an archive file.
:param str dest: Local destination path to install to. If not given, :param str dest: Local destination path to install to. If not given,
installs to `$CHARM_DIR/archives/archive_file_name`. installs to `$CHARM_DIR/archives/archive_file_name`.
:param str checksum: If given, validate the archive file after download. :param str checksum: If given, validate the archive file after download.
:param str hash_type: Algorithm used to generate :param:`checksum`. :param str hash_type: Algorithm used to generate `checksum`.
Can be any hash alrgorithm supported by :mod:`hashlib`, Can be any hash alrgorithm supported by :mod:`hashlib`,
such as md5, sha1, sha256, sha512, etc. such as md5, sha1, sha256, sha512, etc.
""" """
url_parts = self.parse_url(source) url_parts = self.parse_url(source)
dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched') dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched')