From a379e7ed72d9e74675b71f5a18dadb8cafaf33ab Mon Sep 17 00:00:00 2001 From: Ivan Lezhnjov IV Date: Fri, 12 Apr 2013 13:14:03 -0700 Subject: [PATCH 1/5] Updated ansible.spec should now work for CentOS 5 and CentOS 6 Signed-off-by: Ivan Lezhnjov IV --- packaging/rpm/ansible.spec | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/packaging/rpm/ansible.spec b/packaging/rpm/ansible.spec index ccc67d3be25..6b1419cc7ed 100644 --- a/packaging/rpm/ansible.spec +++ b/packaging/rpm/ansible.spec @@ -1,4 +1,5 @@ -%if 0%{?rhel} && 0%{?rhel} <= 5 +%if 0%{?rhel} <= 5 +%define __python /usr/bin/python26 %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot %endif @@ -6,15 +7,15 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Name: ansible Release: 1%{?dist} Summary: SSH-based configuration management, deployment, and task execution system -Version: 1.1 +Version: 1.2rc Group: Development/Libraries License: GPLv3 -Source0: https://github.com/downloads/ansible/ansible/%{name}-%{version}.tar.gz +Source0: https://github.com/downloads/ansible/ansible/%{name}-%{version}.tar.bz2 Url: http://ansible.github.com BuildArch: noarch -%if 0%{?rhel} && 0%{?rhel} <= 5 +%if 0%{?rhel} <= 5 BuildRequires: python26-devel Requires: python26-PyYAML @@ -36,17 +37,13 @@ over SSH and does not require any software or daemons to be installed on remote nodes. Extension modules can be written in any language and are transferred to managed machines automatically. +%if 0%{?rhel} >= 6 %package fireball Summary: Ansible fireball transport support Group: Development/Libraries Requires: %{name} = %{version}-%{release} -%if 0%{?rhel} && 0%{?rhel} <= 5 -Requires: python26-keyczar -Requires: python26-zmq -%else Requires: python-keyczar Requires: python-zmq -%endif %description fireball @@ -57,19 +54,15 @@ multiple actions, but requires additional supporting packages. %package node-fireball Summary: Ansible fireball transport - node end support Group: Development/Libraries -%if 0%{?rhel} && 0%{?rhel} <= 5 -Requires: python26-keyczar -Requires: python26-zmq -%else Requires: python-keyczar Requires: python-zmq -%endif %description node-fireball Ansible can optionally use a 0MQ based transport mechanism, which has additional requirements for nodes to use. This package includes those requirements. +%endif %prep %setup -q @@ -84,7 +77,6 @@ cp examples/hosts $RPM_BUILD_ROOT/etc/ansible/ cp examples/ansible.cfg $RPM_BUILD_ROOT/etc/ansible/ mkdir -p $RPM_BUILD_ROOT/%{_mandir}/{man1,man3}/ cp -v docs/man/man1/*.1 $RPM_BUILD_ROOT/%{_mandir}/man1/ -cp -v docs/man/man3/*.3 $RPM_BUILD_ROOT/%{_mandir}/man3/ mkdir -p $RPM_BUILD_ROOT/%{_datadir}/ansible cp -v library/* $RPM_BUILD_ROOT/%{_datadir}/ansible/ @@ -100,19 +92,21 @@ rm -rf $RPM_BUILD_ROOT %{_datadir}/ansible/f[a-hj-z]* %{_datadir}/ansible/file %config(noreplace) %{_sysconfdir}/ansible -%doc README.md PKG-INFO COPYING +%doc README.md COPYING %doc %{_mandir}/man1/ansible* -%doc %{_mandir}/man3/ansible.[a-eg-z]* -%doc %{_mandir}/man3/ansible.f[a-hj-z]* -%doc %{_mandir}/man3/ansible.file* %doc examples/playbooks +%if 0%{?rhel} <= 5 +%exclude %{_datadir}/ansible/fireball +%endif + +%if 0%{?rhel} >= 6 %files fireball %{_datadir}/ansible/fireball -%doc %{_mandir}/man3/ansible.fireball.* %files node-fireball -%doc README.md PKG-INFO COPYING +%doc README.md COPYING +%endif %changelog From b25e537e7df897b00811d63e9778e06536ec8c41 Mon Sep 17 00:00:00 2001 From: Ivan Lezhnjov IV Date: Fri, 12 Apr 2013 13:26:33 -0700 Subject: [PATCH 2/5] Fixes wrong Source URL Signed-off-by: Ivan Lezhnjov IV --- packaging/rpm/ansible.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/rpm/ansible.spec b/packaging/rpm/ansible.spec index 6b1419cc7ed..425848de13e 100644 --- a/packaging/rpm/ansible.spec +++ b/packaging/rpm/ansible.spec @@ -11,7 +11,7 @@ Version: 1.2rc Group: Development/Libraries License: GPLv3 -Source0: https://github.com/downloads/ansible/ansible/%{name}-%{version}.tar.bz2 +Source0: http://ansible.cc/releases/%{name}-%{version}.tar.bz2 Url: http://ansible.github.com BuildArch: noarch From 4135cffc3f8d81d17201f7f762a2a9bd4f312fd9 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Thu, 21 Mar 2013 21:30:44 +0100 Subject: [PATCH 3/5] Implement python-dmidecode/dmidecode as alternative for kernel DMI This implementation falls back to python-dmidecode (RHEL5.5+) if the kernel as no DMI support. Alternatively, if python-dmidecode is missing, we attempt to use the dmidecode binary (for RHEL5.4 and older) before giving up. This fixes #376 and #1657 and also helps @lwade on RHEL5.5+. --- library/setup | 122 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 31 deletions(-) diff --git a/library/setup b/library/setup index 74799ebca91..fb385468f3a 100644 --- a/library/setup +++ b/library/setup @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . +import os import array import fcntl import fnmatch @@ -29,6 +30,12 @@ import struct import datetime import getpass +if not os.path.exists('/sys/devices/virtual/dmi/id/product_name'): + try: + import dmidecode + except ImportError: + import subprocess + DOCUMENTATION = ''' --- module: setup @@ -390,26 +397,6 @@ class LinuxHardware(Hardware): platform = 'Linux' MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree'] - # DMI bits - DMI_DICT = dict( - form_factor = '/sys/devices/virtual/dmi/id/chassis_type', - product_name = '/sys/devices/virtual/dmi/id/product_name', - product_serial = '/sys/devices/virtual/dmi/id/product_serial', - product_uuid = '/sys/devices/virtual/dmi/id/product_uuid', - product_version = '/sys/devices/virtual/dmi/id/product_version', - system_vendor = '/sys/devices/virtual/dmi/id/sys_vendor', - bios_date = '/sys/devices/virtual/dmi/id/bios_date', - bios_version = '/sys/devices/virtual/dmi/id/bios_version' - ) - # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf - FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop", - "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower", - "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station", - "All In One", "Sub Notebook", "Space-saving", "Lunch Box", - "Main Server Chassis", "Expansion Chassis", "Sub Chassis", - "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis", - "Rack Mount Chassis", "Sealed-case PC", "Multi-system", - "CompactPCI", "AdvancedTCA", "Blade" ] def __init__(self): Hardware.__init__(self) @@ -463,18 +450,91 @@ class LinuxHardware(Hardware): self.facts['processor_cores'] = 'NA' def get_dmi_facts(self): - for (key,path) in LinuxHardware.DMI_DICT.items(): - data = get_file_content(path) - if data is not None: - if key == 'form_factor': - try: - self.facts['form_factor'] = LinuxHardware.FORM_FACTOR[int(data)] - except IndexError, e: - self.facts['form_factor'] = 'unknown (%s)' % data + + def execute(cmd): + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = p.communicate() + if p.returncode or err: + return None + return out.rstrip() + + if os.path.exists('/sys/devices/virtual/dmi/id/product_name'): + # Use kernel DMI info, if available + + # DMI SPEC -- http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf + FORM_FACTOR = [ "Unknown", "Other", "Unknown", "Desktop", + "Low Profile Desktop", "Pizza Box", "Mini Tower", "Tower", + "Portable", "Laptop", "Notebook", "Hand Held", "Docking Station", + "All In One", "Sub Notebook", "Space-saving", "Lunch Box", + "Main Server Chassis", "Expansion Chassis", "Sub Chassis", + "Bus Expansion Chassis", "Peripheral Chassis", "RAID Chassis", + "Rack Mount Chassis", "Sealed-case PC", "Multi-system", + "CompactPCI", "AdvancedTCA", "Blade" ] + + DMI_DICT = dict( + bios_date = '/sys/devices/virtual/dmi/id/bios_date', + bios_version = '/sys/devices/virtual/dmi/id/bios_version', + form_factor = '/sys/devices/virtual/dmi/id/chassis_type', + product_name = '/sys/devices/virtual/dmi/id/product_name', + product_serial = '/sys/devices/virtual/dmi/id/product_serial', + product_uuid = '/sys/devices/virtual/dmi/id/product_uuid', + product_version = '/sys/devices/virtual/dmi/id/product_version', + system_vendor = '/sys/devices/virtual/dmi/id/sys_vendor', + ) + + for (key,path) in DMI_DICT.items(): + data = get_file_content(path) + if data is not None: + if key == 'form_factor': + try: + self.facts['form_factor'] = FORM_FACTOR[int(data)] + except IndexError, e: + self.facts['form_factor'] = 'unknown (%s)' % data + else: + self.facts[key] = data else: - self.facts[key] = data - else: - self.facts[key] = 'NA' + self.facts[key] = 'NA' + + elif 'dmidecode' in sys.modules.keys(): + # Use python dmidecode, if available + + DMI_DICT = dict( + bios_date = '/dmidecode/BIOSinfo/ReleaseDate', + bios_version = '/dmidecode/BIOSinfo/BIOSrevision', + form_factor = '/dmidecode/ChassisInfo/ChassisType', + product_name = '/dmidecode/SystemInfo/ProductName', + product_serial = '/dmidecode/SystemInfo/SerialNumber', + product_uuid = '/dmidecode/SystemInfo/SystemUUID', + product_version = '/dmidecode/SystemInfo/Version', + system_vendor = '/dmidecode/SystemInfo/Manufacturer', + ) + + dmixml = dmidecode.dmidecodeXML() + dmixml.SetResultType(dmidecode.DMIXML_DOC) + xmldoc = dmixml.QuerySection('all') + dmixp = xmldoc.xpathNewContext() + + for (key,path) in DMI_DICT.items(): + try: + data = dmixp.xpathEval(path) + if len(data) > 0: + self.facts[key] = data[0].get_content() + else: + self.facts[key] = 'Error' + except: + self.facts[key] = 'NA' + + else: + # Fall back to using dmidecode, if available + + self.facts['bios_date'] = execute('dmidecode -s bios-release-date') or 'NA' + self.facts['bios_version'] = execute('dmidecode -s bios-version') or 'NA' + self.facts['form_factor'] = execute('dmidecode -s chassis-type') or 'NA' + self.facts['product_name'] = execute('dmidecode -s system-product-name') or 'NA' + self.facts['product_serial'] = execute('dmidecode -s system-serial-number') or 'NA' + self.facts['product_uuid'] = execute('dmidecode -s system-uuid') or 'NA' + self.facts['product_version'] = execute('dmidecode -s system-version') or 'NA' + self.facts['system_vendor'] = execute('dmidecode -s system-manufacturer') or 'NA' def get_mount_facts(self): self.facts['mounts'] = [] From d2e457f81f9432f3f4637af364550ee64528b319 Mon Sep 17 00:00:00 2001 From: Aaron Brady Date: Tue, 23 Apr 2013 14:05:01 +0100 Subject: [PATCH 4/5] Accept numeric user and group parameters --- lib/ansible/module_common.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index c5bb9d46b41..a57033910b7 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -369,9 +369,12 @@ class AnsibleModule(object): user, group = self.user_and_group(path) if owner != user: try: - uid = pwd.getpwnam(owner).pw_uid - except KeyError: - self.fail_json(path=path, msg='chown failed: failed to look up user %s' % owner) + uid = int(owner) + except ValueError: + try: + uid = pwd.getpwnam(owner).pw_uid + except KeyError: + self.fail_json(path=path, msg='chown failed: failed to look up user %s' % owner) if self.check_mode: return True try: @@ -390,9 +393,12 @@ class AnsibleModule(object): if self.check_mode: return True try: - gid = grp.getgrnam(group).gr_gid - except KeyError: - self.fail_json(path=path, msg='chgrp failed: failed to look up group %s' % group) + gid = int(group) + except ValueError: + try: + gid = grp.getgrnam(group).gr_gid + except KeyError: + self.fail_json(path=path, msg='chgrp failed: failed to look up group %s' % group) try: os.chown(path, -1, gid) except OSError: From bd226eb826a396e5cab60330b0ce1ed9741a7f3c Mon Sep 17 00:00:00 2001 From: Aaron Brady Date: Tue, 23 Apr 2013 15:29:35 +0100 Subject: [PATCH 5/5] Refactor user_and_group so it works off of numbers There are various cases where a UID to username to UID mapping breaks down. One UID can be used by two usernames, or no username. If we always use UIDs internally, then these ambiguous cases won't be a problem. --- lib/ansible/module_common.py | 64 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index a57033910b7..9d5efdef7fc 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -319,15 +319,7 @@ class AnsibleModule(object): st = os.stat(filename) uid = st.st_uid gid = st.st_gid - try: - user = pwd.getpwuid(uid)[0] - except KeyError: - user = str(uid) - try: - group = grp.getgrgid(gid)[0] - except KeyError: - group = str(gid) - return (user, group) + return (uid, gid) def set_default_selinux_context(self, path, changed): if not HAVE_SELINUX or not self.selinux_enabled(): @@ -366,17 +358,17 @@ class AnsibleModule(object): path = os.path.expanduser(path) if owner is None: return changed - user, group = self.user_and_group(path) - if owner != user: + orig_uid, orig_gid = self.user_and_group(path) + try: + uid = int(owner) + except ValueError: try: - uid = int(owner) - except ValueError: - try: - uid = pwd.getpwnam(owner).pw_uid - except KeyError: - self.fail_json(path=path, msg='chown failed: failed to look up user %s' % owner) - if self.check_mode: - return True + uid = pwd.getpwnam(owner).pw_uid + except KeyError: + self.fail_json(path=path, msg='chown failed: failed to look up user %s' % owner) + if self.check_mode: + return True + if orig_uid != uid: try: os.chown(path, uid, -1) except OSError: @@ -388,17 +380,17 @@ class AnsibleModule(object): path = os.path.expanduser(path) if group is None: return changed - old_user, old_group = self.user_and_group(path) - if old_group != group: - if self.check_mode: - return True + orig_uid, orig_gid = self.user_and_group(path) + try: + gid = int(group) + except ValueError: try: - gid = int(group) - except ValueError: - try: - gid = grp.getgrnam(group).gr_gid - except KeyError: - self.fail_json(path=path, msg='chgrp failed: failed to look up group %s' % group) + gid = grp.getgrnam(group).gr_gid + except KeyError: + self.fail_json(path=path, msg='chgrp failed: failed to look up group %s' % group) + if self.check_mode: + return True + if orig_gid != gid: try: os.chown(path, -1, gid) except OSError: @@ -478,8 +470,18 @@ class AnsibleModule(object): if path is None: return kwargs if os.path.exists(path): - (user, group) = self.user_and_group(path) - kwargs['owner'] = user + (uid, gid) = self.user_and_group(path) + kwargs['uid'] = uid + kwargs['gid'] = gid + try: + user = pwd.getpwuid(uid)[0] + except KeyError: + user = str(uid) + try: + group = grp.getgrgid(gid)[0] + except KeyError: + group = str(gid) + kwargs['owner'] = user kwargs['group'] = group st = os.stat(path) kwargs['mode'] = oct(stat.S_IMODE(st[stat.ST_MODE]))