Commit c4b5e460 by James Cammarata

Backporting apt_repository module from devel

parent f1f63829
...@@ -28,7 +28,7 @@ short_description: Add and remove APT repositores ...@@ -28,7 +28,7 @@ short_description: Add and remove APT repositores
description: description:
- Add or remove an APT repositories in Ubuntu and Debian. - Add or remove an APT repositories in Ubuntu and Debian.
notes: notes:
- This module works on Debian and Ubuntu and requires C(python-apt) and C(python-pycurl) packages. - This module works on Debian and Ubuntu and requires C(python-apt).
- This module supports Debian Squeeze (version 6) as well as its successors. - This module supports Debian Squeeze (version 6) as well as its successors.
- This module treats Debian and Ubuntu distributions separately. So PPA could be installed only on Ubuntu machines. - This module treats Debian and Ubuntu distributions separately. So PPA could be installed only on Ubuntu machines.
options: options:
...@@ -43,15 +43,21 @@ options: ...@@ -43,15 +43,21 @@ options:
default: "present" default: "present"
description: description:
- A source string state. - A source string state.
mode:
required: false
default: 0644
description:
- The octal mode for newly created files in sources.list.d
version_added: "1.6"
update_cache: update_cache:
description: description:
- Run the equivalent of C(apt-get update) if has changed. - Run the equivalent of C(apt-get update) when a change occurs. Cache updates are run after making changes.
required: false required: false
default: "yes" default: "yes"
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
author: Alexander Saltanov author: Alexander Saltanov
version_added: "0.7" version_added: "0.7"
requirements: [ python-apt, python-pycurl ] requirements: [ python-apt ]
''' '''
EXAMPLES = ''' EXAMPLES = '''
...@@ -70,10 +76,6 @@ apt_repository: repo='ppa:nginx/stable' ...@@ -70,10 +76,6 @@ apt_repository: repo='ppa:nginx/stable'
''' '''
import glob import glob
try:
import json
except ImportError:
import simplejson as json
import os import os
import re import re
import tempfile import tempfile
...@@ -87,22 +89,19 @@ try: ...@@ -87,22 +89,19 @@ try:
except ImportError: except ImportError:
HAVE_PYTHON_APT = False HAVE_PYTHON_APT = False
try:
import pycurl
HAVE_PYCURL = True
except ImportError:
HAVE_PYCURL = False
VALID_SOURCE_TYPES = ('deb', 'deb-src') VALID_SOURCE_TYPES = ('deb', 'deb-src')
def install_python_apt(module):
class CurlCallback: if not module.check_mode:
def __init__(self): apt_get_path = module.get_bin_path('apt-get')
self.contents = '' if apt_get_path:
rc, so, se = module.run_command('%s update && %s install python-apt -y -q' % (apt_get_path, apt_get_path))
def body_callback(self, buf): if rc == 0:
self.contents = self.contents + buf global apt, apt_pkg
import apt
import apt_pkg
class InvalidSource(Exception): class InvalidSource(Exception):
pass pass
...@@ -140,12 +139,22 @@ class SourcesList(object): ...@@ -140,12 +139,22 @@ class SourcesList(object):
def _suggest_filename(self, line): def _suggest_filename(self, line):
def _cleanup_filename(s): def _cleanup_filename(s):
return '_'.join(re.sub('[^a-zA-Z0-9]', ' ', s).split()) return '_'.join(re.sub('[^a-zA-Z0-9]', ' ', s).split())
def _strip_username_password(s):
if '@' in s:
s = s.split('@', 1)
s = s[-1]
return s
# Drop options and protocols. # Drop options and protocols.
line = re.sub('\[[^\]]+\]', '', line) line = re.sub('\[[^\]]+\]', '', line)
line = re.sub('\w+://', '', line) line = re.sub('\w+://', '', line)
# split line into valid keywords
parts = [part for part in line.split() if part not in VALID_SOURCE_TYPES] parts = [part for part in line.split() if part not in VALID_SOURCE_TYPES]
# Drop usernames and passwords
parts[0] = _strip_username_password(parts[0])
return '%s.list' % _cleanup_filename(' '.join(parts[:1])) return '%s.list' % _cleanup_filename(' '.join(parts[:1]))
def _parse(self, line, raise_if_invalid_or_disabled=False): def _parse(self, line, raise_if_invalid_or_disabled=False):
...@@ -214,7 +223,10 @@ class SourcesList(object): ...@@ -214,7 +223,10 @@ class SourcesList(object):
if sources: if sources:
d, fn = os.path.split(filename) d, fn = os.path.split(filename)
fd, tmp_path = tempfile.mkstemp(prefix=".%s-" % fn, dir=d) fd, tmp_path = tempfile.mkstemp(prefix=".%s-" % fn, dir=d)
os.chmod(os.path.join(fd, tmp_path), 0644)
# allow the user to override the default mode
this_mode = module.params['mode']
module.set_mode_if_different(tmp_path, this_mode, False)
f = os.fdopen(fd, 'w') f = os.fdopen(fd, 'w')
for n, valid, enabled, source, comment in sources: for n, valid, enabled, source, comment in sources:
...@@ -290,29 +302,19 @@ class SourcesList(object): ...@@ -290,29 +302,19 @@ class SourcesList(object):
class UbuntuSourcesList(SourcesList): class UbuntuSourcesList(SourcesList):
LP_API = 'https://launchpad.net/api/1.0/~%s/+archive/%s' LP_API = 'https://launchpad.net/api/1.0/~%s/+archive/%s'
def __init__(self, add_ppa_signing_keys_callback=None): def __init__(self, module, add_ppa_signing_keys_callback=None):
self.module = module
self.add_ppa_signing_keys_callback = add_ppa_signing_keys_callback self.add_ppa_signing_keys_callback = add_ppa_signing_keys_callback
super(UbuntuSourcesList, self).__init__() super(UbuntuSourcesList, self).__init__()
def _get_ppa_info(self, owner_name, ppa_name): def _get_ppa_info(self, owner_name, ppa_name):
# we can not use urllib2 here as it does not do cert verification
lp_api = self.LP_API % (owner_name, ppa_name) lp_api = self.LP_API % (owner_name, ppa_name)
return self._get_ppa_info_curl(lp_api)
headers = dict(Accept='application/json')
def _get_ppa_info_curl(self, lp_api): response, info = fetch_url(self.module, lp_api, headers=headers)
callback = CurlCallback() return json.load(response)
curl = pycurl.Curl()
curl.setopt(pycurl.SSL_VERIFYPEER, 1)
curl.setopt(pycurl.SSL_VERIFYHOST, 2)
curl.setopt(pycurl.WRITEFUNCTION, callback.body_callback)
curl.setopt(pycurl.URL, str(lp_api))
curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])
curl.perform()
curl.close()
lp_page = callback.contents
return json.loads(lp_page)
def _expand_ppa(self, path): def _expand_ppa(self, path):
ppa = path.split(':')[1] ppa = path.split(':')[1]
...@@ -363,16 +365,17 @@ def main(): ...@@ -363,16 +365,17 @@ def main():
argument_spec=dict( argument_spec=dict(
repo=dict(required=True), repo=dict(required=True),
state=dict(choices=['present', 'absent'], default='present'), state=dict(choices=['present', 'absent'], default='present'),
mode=dict(required=False, default=0644),
update_cache = dict(aliases=['update-cache'], type='bool', default='yes'), update_cache = dict(aliases=['update-cache'], type='bool', default='yes'),
# this should not be needed, but exists as a failsafe
install_python_apt=dict(required=False, default="yes", type='bool'),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
if not HAVE_PYTHON_APT: params = module.params
module.fail_json(msg='Could not import python modules: apt_pkg. Please install python-apt package.') if params['install_python_apt'] and not HAVE_PYTHON_APT and not module.check_mode:
install_python_apt(module)
if not HAVE_PYCURL:
module.fail_json(msg='Could not import python modules: pycurl. Please install python-pycurl package.')
repo = module.params['repo'] repo = module.params['repo']
state = module.params['state'] state = module.params['state']
...@@ -380,7 +383,8 @@ def main(): ...@@ -380,7 +383,8 @@ def main():
sourceslist = None sourceslist = None
if isinstance(distro, aptsources.distro.UbuntuDistribution): if isinstance(distro, aptsources.distro.UbuntuDistribution):
sourceslist = UbuntuSourcesList(add_ppa_signing_keys_callback=get_add_ppa_signing_key_callback(module)) sourceslist = UbuntuSourcesList(module,
add_ppa_signing_keys_callback=get_add_ppa_signing_key_callback(module))
elif isinstance(distro, aptsources.distro.DebianDistribution) or \ elif isinstance(distro, aptsources.distro.DebianDistribution) or \
isinstance(distro, aptsources.distro.Distribution): isinstance(distro, aptsources.distro.Distribution):
sourceslist = SourcesList() sourceslist = SourcesList()
...@@ -413,5 +417,6 @@ def main(): ...@@ -413,5 +417,6 @@ def main():
# import module snippets # import module snippets
from ansible.module_utils.basic import * from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
main() main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment