def __add_bare_helper(openstack_release, pocket_format, final_function): """Helper for _add_bare_openstack[_proposed] The bulk of the work between the two functions is exactly the same except for the pocket format and the function that is run if it's the distro version. :param openstack_release: the OpenStack codename. e.g. ussuri :type openstack_release: str :param pocket_format: the pocket formatter string to construct a pocket str from the openstack_release and the current ubuntu version. :type pocket_format: str :param final_function: the function to call if it is the distro version. :type final_function: Callable :raises SourceConfigError on error """ ubuntu_version = get_distrib_codename() possible_pocket = pocket_format.format(ubuntu_version, openstack_release) if possible_pocket in CLOUD_ARCHIVE_POCKETS: _add_cloud_pocket(possible_pocket) return # Otherwise it's almost certainly the distro version; verify that it # exists. try: assert UBUNTU_OPENSTACK_RELEASE[ubuntu_version] == openstack_release except KeyError: raise SourceConfigError( "Invalid ubuntu version {} isn't known to this library".format( ubuntu_version)) except AssertionError: raise SourceConfigError( 'Invalid OpenStack release specified: {} for Ubuntu version {}'. format(openstack_release, ubuntu_version)) final_function()
def _verify_is_ubuntu_rel(release, os_release): """Verify that the release is in the same as the current ubuntu release. :param release: String, lowercase for the release. :param os_release: String, the os_release being asked for :raises: SourceConfigError if the release is not the same as the ubuntu release. """ ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] if release != ubuntu_rel: raise SourceConfigError( 'Invalid Cloud Archive release specified: {}-{} on this Ubuntu' 'version ({})'.format(release, os_release, ubuntu_rel))
def _add_proposed(): """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list Uses lsb_release()['DISTRIB_CODENAME'] to determine the correct staza for the deb line. For intel architecutres PROPOSED_POCKET is used for the release, but for other architectures PROPOSED_PORTS_POCKET is used for the release. """ release = lsb_release()['DISTRIB_CODENAME'] arch = platform.machine() if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET): raise SourceConfigError( "Arch {} not supported for (distro-)proposed".format(arch)) with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
def _add_proposed(): """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list Uses get_distrib_codename to determine the correct stanza for the deb line. For Intel architectures PROPOSED_POCKET is used for the release, but for other architectures PROPOSED_PORTS_POCKET is used for the release. """ release = get_distrib_codename() arch = platform.machine() if arch not in ARCH_TO_PROPOSED_POCKET.keys(): raise SourceConfigError( "Arch {} not supported for (distro-)proposed".format(arch)) with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
def _add_cloud_pocket(pocket): """Add a cloud pocket as /etc/apt/sources.d/cloud-archive.list Note that this overwrites the existing file if there is one. This function also converts the simple pocket in to the actual pocket using the CLOUD_ARCHIVE_POCKETS mapping. :param pocket: string representing the pocket to add a deb spec for. :raises: SourceConfigError if the cloud pocket doesn't exist or the requested release doesn't match the current distro version. """ apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), fatal=True) if pocket not in CLOUD_ARCHIVE_POCKETS: raise SourceConfigError('Unsupported cloud: source option %s' % pocket) actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: apt.write(CLOUD_ARCHIVE.format(actual_pocket))
def add_source(source, key=None, fail_invalid=False): """Add a package source to this system. @param source: a URL or sources.list entry, as supported by add-apt-repository(1). Examples:: ppa:charmers/example deb https://stub:[email protected]/ubuntu trusty main In addition: 'proposed:' may be used to enable the standard 'proposed' pocket for the release. 'cloud:' may be used to activate official cloud archive pockets, such as 'cloud:icehouse' 'distro' may be used as a noop Full list of source specifications supported by the function are: 'distro': A NOP; i.e. it has no effect. 'proposed': the proposed deb spec [2] is wrtten to /etc/apt/sources.list/proposed 'distro-proposed': adds <version>-proposed to the debs [2] 'ppa:<ppa-name>': add-apt-repository --yes <ppa_name> 'deb <deb-spec>': add-apt-repository --yes deb <deb-spec> 'http://....': add-apt-repository --yes http://... 'cloud-archive:<spec>': add-apt-repository -yes cloud-archive:<spec> 'cloud:<release>[-staging]': specify a Cloud Archive pocket <release> with optional staging version. If staging is used then the staging PPA [2] with be used. If staging is NOT used then the cloud archive [3] will be added, and the 'ubuntu-cloud-keyring' package will be added for the current distro. Otherwise the source is not recognised and this is logged to the juju log. However, no error is raised, unless sys_error_on_exit is True. [1] deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main where {} is replaced with the derived pocket name. [2] deb http://archive.ubuntu.com/ubuntu {}-proposed \ main universe multiverse restricted where {} is replaced with the lsb_release codename (e.g. xenial) [3] deb http://ubuntu-cloud.archive.canonical.com/ubuntu <pocket> to /etc/apt/sources.list.d/cloud-archive-list @param key: A key to be added to the system's APT keyring and used to verify the signatures on packages. Ideally, this should be an ASCII format GPG public key including the block headers. A GPG key id may also be used, but be aware that only insecure protocols are available to retrieve the actual public key from a public keyserver placing your Juju environment at risk. ppa and cloud archive keys are securely added automtically, so sould not be provided. @param fail_invalid: (boolean) if True, then the function raises a SourceConfigError is there is no matching installation source. @raises SourceConfigError() if for cloud:<pocket>, the <pocket> is not a valid pocket in CLOUD_ARCHIVE_POCKETS """ _mapping = OrderedDict([ (r"^distro$", lambda: None), # This is a NOP (r"^(?:proposed|distro-proposed)$", _add_proposed), (r"^cloud-archive:(.*)$", _add_apt_repository), (r"^((?:deb |http:|https:|ppa:).*)$", _add_apt_repository), (r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging), (r"^cloud:(.*)-(.*)$", _add_cloud_distro_check), (r"^cloud:(.*)$", _add_cloud_pocket), (r"^snap:.*-(.*)-(.*)$", _add_cloud_distro_check), ]) if source is None: source = '' for r, fn in six.iteritems(_mapping): m = re.match(r, source) if m: # call the assoicated function with the captured groups # raises SourceConfigError on error. fn(*m.groups()) if key: try: import_key(key) except GPGKeyError as e: raise SourceConfigError(str(e)) break else: # nothing matched. log an error and maybe sys.exit err = "Unknown source: {!r}".format(source) log(err) if fail_invalid: raise SourceConfigError(err)
def add_source(source, key=None): """Add a package source to this system. @param source: a URL or sources.list entry, as supported by add-apt-repository(1). Examples:: ppa:charmers/example deb https://stub:[email protected]/ubuntu trusty main In addition: 'proposed:' may be used to enable the standard 'proposed' pocket for the release. 'cloud:' may be used to activate official cloud archive pockets, such as 'cloud:icehouse' 'distro' may be used as a noop @param key: A key to be added to the system's APT keyring and used to verify the signatures on packages. Ideally, this should be an ASCII format GPG public key including the block headers. A GPG key id may also be used, but be aware that only insecure protocols are available to retrieve the actual public key from a public keyserver placing your Juju environment at risk. ppa and cloud archive keys are securely added automtically, so sould not be provided. """ if source is None: log('Source is not present. Skipping') return if (source.startswith('ppa:') or source.startswith('http') or source.startswith('deb ') or source.startswith('cloud-archive:')): subprocess.check_call(['add-apt-repository', '--yes', source]) elif source.startswith('cloud:'): install(filter_installed_packages(['ubuntu-cloud-keyring']), fatal=True) pocket = source.split(':')[-1] if pocket not in CLOUD_ARCHIVE_POCKETS: raise SourceConfigError('Unsupported cloud: source option %s' % pocket) actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: apt.write(CLOUD_ARCHIVE.format(actual_pocket)) elif source == 'proposed': release = lsb_release()['DISTRIB_CODENAME'] with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: apt.write(PROPOSED_POCKET.format(release)) elif source == 'distro': pass else: log("Unknown source: {!r}".format(source)) if key: if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: with NamedTemporaryFile('w+') as key_file: key_file.write(key) key_file.flush() key_file.seek(0) subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file) else: # Note that hkp: is in no way a secure protocol. Using a # GPG key id is pointless from a security POV unless you # absolutely trust your network and DNS. subprocess.check_call([ 'apt-key', 'adv', '--keyserver', 'hkp://keyserver.ubuntu.com:80', '--recv', key ])