Added AIX support

Dear List,

A collegue of mine has added basix AIX support to the setup, user and group modules.
We have no access to GIT behind the corporate firewall I’m afraid and he has no interest in using GIT anyway :wink:

Is it helpfull to just add the diff’s here?

setup module

mmaas@xmgtansible:~/playbooks$ diff library/setup /usr/share/ansible/setup

122,125d121

< if self.facts[‘system’] == ‘AIX’:

< rc, out, err = module.run_command(“/usr/sbin/bootinfo -p”)

< data = out.split(‘\n’)

< self.facts[‘architecture’] = data[0]

147,152d142

< if self.facts[‘system’] == ‘AIX’:

< self.facts[‘distribution’] = ‘AIX’

< rc, out, err = module.run_command(“/usr/bin/oslevel”)

< data = out.split(‘.’)

< self.facts[‘distribution_version’] = data[0]

< self.facts[‘distribution_release’] = data[1]

612,677d601

<

< class AIX(Hardware):

< “”"

< AIX-specific subclass of Hardware. Defines memory and CPU facts:

< - memfree_mb

< - memtotal_mb

< - swapfree_mb

< - swaptotal_mb

< - processor (a list)

< - processor_cores

< - processor_count

< “”"

< platform = ‘AIX’

<

< def init(self):

< Hardware.init(self)

<

< def populate(self):

< self.get_cpu_facts()

< self.get_memory_facts()

< self.get_dmi_facts()

< return self.facts

<

< def get_cpu_facts(self):

< self.facts[‘processor’] =

< rc, out, err = module.run_command(“/usr/sbin/lsattr -El proc0 -a type”)

< data = out.split(’ ')

< self.facts[‘processor’] = data[1]

< rc, out, err = module.run_command(“/usr/sbin/lsattr -El proc0 -a smt_threads”)

< data = out.split(’ ')

< self.facts[‘processor_cores’] = int(data[1])

< rc, out, err = module.run_command(“/usr/sbin/lsdev -Cc processor”)

< i = 0

< for line in out.split(‘\n’):

< data = line.split(‘:’)

< if ‘Available’ in line:

< i += 1

< self.facts[‘processor_count’] = int(i)

<

< def get_memory_facts(self):

< pagesize = 4096

< rc, out, err = module.run_command(“/usr/bin/vmstat -v”)

< for line in out.split(‘\n’):

< data = line.split()

< if ‘memory pages’ in line:

< pagecount = long(data[0])

< if ‘free pages’ in line:

< freecount = long(data[0])

< self.facts[‘memtotal_mb’] = pagesize * pagecount / 1024 / 1024

< self.facts[‘memfree_mb’] = pagesize * freecount / 1024 / 1024

< # Get swapinfo. swapinfo output looks like:

< # Device 1M-blocks Used Avail Capacity

< # /dev/ada0p3 314368 0 314368 0%

< #

< rc, out, err = module.run_command(“/usr/sbin/lsps -s”)

< lines = out.split(‘\n’)

< data = lines[1].split()

< swaptotal_mb = long(data[0].rstrip(‘MB’))

< percused = int(data[1].rstrip(‘%’))

< self.facts[‘swaptotal_mb’] = swaptotal_mb

< self.facts[‘swapfree_mb’] = long(swaptotal_mb * ( 100 - percused ) / 100)

<

< def get_dmi_facts(self):

< rc, out, err = module.run_command(“/usr/sbin/lsattr -El sys0 -a fwversion”)

< data = out.split()

< self.facts[‘firmware_version’] = data[1].strip(‘IBM,’)

user module

mmaas@xmgtansible:~/playbooks$ diff library/user /usr/share/ansible/user

810,962d809

< class AIX(User):

< “”"

< This is a AIX User manipulation class.

<

< This overrides the following methods from the generic class:-

< - create_user()

< - remove_user()

< - modify_user()

< “”"

<

< platform = ‘AIX’

< distribution = None

< SHADOWFILE = ‘/etc/security/passwd’

<

< def remove_user(self):

< cmd = [self.module.get_bin_path(‘userdel’, True)]

< if self.remove:

< cmd.append(‘-r’)

< cmd.append(self.name)

<

< return self.execute_command(cmd)

<

< def create_user_useradd(self, command_name=‘useradd’):

< cmd = [self.module.get_bin_path(command_name, True)]

<

< if self.uid is not None:

< cmd.append(‘-u’)

< cmd.append(self.uid)

<

< if self.group is not None:

< if not self.group_exists(self.group):

< self.module.fail_json(msg=“Group %s does not exist” % self.group)

< cmd.append(‘-g’)

< cmd.append(self.group)

<

< if self.groups is not None:

< for g in self.groups.split(‘,’):

< if not self.group_exists(g):

< self.module.fail_json(msg=“Group %s does not exist” % (g))

< cmd.append(‘-G’)

< cmd.append(self.groups)

<

< if self.comment is not None:

< cmd.append(‘-c’)

< cmd.append(self.comment)

<

< if self.home is not None:

< cmd.append(‘-d’)

< cmd.append(self.home)

<

< if self.shell is not None:

< cmd.append(‘-s’)

< cmd.append(self.shell)

<

< if self.createhome:

< cmd.append(‘-m’)

<

< if self.system:

< cmd.append(‘-r’)

<

< cmd.append(self.name)

< (rc, out, err) = self.execute_command(cmd)

<

< # set password with chpasswd

< if self.password is not None:

< cmd =

< cmd.append(‘echo "’+self.name+‘:’+self.password+‘" |’)

< cmd.append(self.module.get_bin_path(‘chpasswd’, True))

< cmd.append(‘-e’)

< cmd.append(‘-c’)

< self.execute_command(’ '.join(cmd))

<

< return (rc, out, err)

<

< def modify_user_usermod(self):

< cmd = [self.module.get_bin_path(‘usermod’, True)]

< info = self.user_info()

<

< if self.uid is not None and info[2] != int(self.uid):

< cmd.append(‘-u’)

< cmd.append(self.uid)

<

< if self.group is not None:

< if not self.group_exists(self.group):

< self.module.fail_json(msg=“Group %s does not exist” % self.group)

< ginfo = self.group_info(self.group)

< if info[3] != ginfo[2]:

< cmd.append(‘-g’)

< cmd.append(self.group)

<

< if self.groups is not None:

< current_groups = self.user_group_membership()

< groups = self.groups.split(‘,’)

< for g in groups:

< if not self.group_exists(g):

< self.module.fail_json(msg=“Group %s does not exist” % (g))

<

< group_diff = set(sorted(current_groups)).symmetric_difference(set(sorted(groups)))

< groups_need_mod = False

<

< if group_diff:

< if self.append:

< for g in groups:

< if g in group_diff:

< groups.extend(current_groups)

< set(groups)

< groups_need_mod = True

< break

< else:

< groups_need_mod = True

<

< if groups_need_mod:

< cmd.append(‘-G’)

< cmd.append(‘,’.join(groups))

<

< if self.comment is not None and info[4] != self.comment:

< cmd.append(‘-c’)

< cmd.append(self.comment)

<

< if self.home is not None and info[5] != self.home:

< cmd.append(‘-d’)

< cmd.append(self.home)

<

< if self.shell is not None and info[6] != self.shell:

< cmd.append(‘-s’)

< cmd.append(self.shell)

<

<

< # skip if no changes to be made

< if len(cmd) == 1:

< (rc, out, err) = (None, ‘’, ‘’)

< else:

< cmd.append(self.name)

< (rc, out, err) = self.execute_command(cmd)

<

< # set password with chpasswd

< if self.password is not None and info[1] != self.password:

< cmd =

< cmd.append(‘echo "’+self.name+‘:’+self.password+‘" |’)

< cmd.append(self.module.get_bin_path(‘chpasswd’, True))

< cmd.append(‘-e’)

< cmd.append(‘-c’)

< (rc2, out2, err2) = self.execute_command(’ '.join(cmd))

< else:

< (rc2, out2, err2) = (None, ‘’, ‘’)

<

< if rc != None:

< return (rc, out+out2, err+err2)

< else:

< return (rc2, out+out2, err+err2)

<

< # ===========================================

<

group module

mmaas@xmgtansible:~/playbooks$ diff library/group /usr/share/ansible/group

163,204d162

<

< # ===========================================

<

< class AIX(Group):

< “”"

< This is a AIX Group manipulation class.

<

< This overrides the following methods from the generic class:-

< - group_del()

< - group_add()

< - group_mod()

< “”"

<

< platform = ‘AIX’

< distribution = None

< GROUPFILE = ‘/etc/group’

<

< def group_del(self):

< cmd = [self.module.get_bin_path(‘rmgroup’, True), self.name]

< return self.execute_command(cmd)

<

< def group_add(self, **kwargs):

< cmd = [self.module.get_bin_path(‘mkgroup’, True)]

< for key in kwargs:

< if key == ‘gid’ and kwargs[key] is not None:

< cmd.append(‘id=’+kwargs[key])

< elif key == ‘system’ and kwargs[key] == ‘yes’:

< cmd.append(‘-a’)

< cmd.append(self.name)

< return self.execute_command(cmd)

<

< def group_mod(self, **kwargs):

< cmd = [self.module.get_bin_path(‘chgroup’, True)]

< info = self.group_info()

< for key in kwargs:

< if key == ‘gid’:

< if kwargs[key] is not None and info[2] != int(kwargs[key]):

< cmd.append(‘id=’+kwargs[key])

< if len(cmd) == 1:

< return (None, ‘’, ‘’)

< cmd.append(self.name)

< return self.execute_command(cmd)

We have tested this on AIX 5.3 and 6.1 and it works “as advertised”

Thanks,
Mark

I can graft those pieces in and submit a pull request for them, but unfortunately cannot test them, although if there is anyone else on the list who can test AIX and would like me to do the integration I can do that.

A couple of questions...

   - What version of ansible are you working against

   - Any chance you could just send me the files - the
     HTML email conversion of a diff may have unexpected
     damage to the diff, which with my inability to test
     is not a good thing! Alternatively a unified diff
     (diff -u) in plain text would be good.

  Nigel.

Mark Maas wrote:

A collegue of mine has added basix AIX support to the setup, user and
group modules.
We have no access to GIT behind the corporate firewall I'm afraid and he
has no interest in using GIT anyway :wink:

[snip of diffs]

Hi,

I’ve sent the files in a different email, and as for the version:

mmaas@xmgtansible:~/playbooks$ ansible --version
ansible 1.1

Thanks!
Mark

It is certainly better than not sharing it at all :slight_smile:

I have made the pull-request in your behalf:

     https://github.com/ansible/ansible/pull/2083

It would have helped considerably if the diff was in unified diff format, however I assume this was done on AIX (without GNU diff :-)).

PS I made one change related to get_distribution_facts(), if we know what to do for AIX, we can avoid doing the default first. All the other stuff is cleanly separated so I won't touch it. Please test !

We’re talking old farts here :wink: Old-AIX-users… They do not like all that new fangled GIT stuff…
Luckily the Colleague in this case has an open mind.

Here are the diffs and the adjusted modules:
https://dl.dropbox.com/u/7875588/reansibleprojectaddedaixsupport.zip

I already made the pull request based on your initial email, so unless you changed anything in these files, they are no longer needed. See the link I send you:

     https://github.com/ansible/ansible/pull/2083

Here are the diffs and the adjusted modules:

https://dl.dropbox.com/u/7875588/reansibleprojectaddedaixsupport.zip

I already made the pull request based on your initial email, so unless you
changed anything in these files, they are no longer needed. See the link I
send you:

Excellent, thanks, the diff’s are now unified versions. That’s all. But too little too late I guess :wink:

Thanks Dag!
Mark

Hugely interesting, I'll probably get around to merging the pull
request this weekend.

If Linux remains happy, that's good, and we'll let others test the AIX!

Not even puppet / Chef have this kind of AIX support :wink:

Fixed an error in the get_cpu_facts of the AIX class (assumed proc0 as the first processor but can be different in a LPAR environment):

def get_cpu_facts(self):
self.facts[‘processor’] =
rc, out, err = module.run_command(“/usr/sbin/lsdev -Cc processor”)
i = 0
for line in out.split(‘\n’):
if ‘Available’ in line:
if i == 0:
data = line.split(’ ‘)
cpudev = data[0]
i += 1
self.facts[‘processor_count’] = int(i)
rc, out, err = module.run_command(“/usr/sbin/lsattr -El " + cpudev + " -a type”)
data = out.split(’ ‘)
self.facts[‘processor’] = data[1]
rc, out, err = module.run_command(“/usr/sbin/lsattr -El " + cpudev + " -a smt_threads”)
data = out.split(’ ')
self.facts[‘processor_cores’] = int(data[1])

Added AIX class in the service module to control AIX SRC processes.
Class is added at line 849:

===========================================

Subclass: AIX

class AIX(Service):
“”"
This is the AIX Service (SRC) manipulation class - it uses lssrc, startsrc, stopsrc
and refresh for service control. Enabling a service is currently not supported.
Would require to add an entry in the /etc/inittab file (mkitab, chitab and rmitab
commands)
“”"

platform = ‘AIX’
distribution = None

def get_service_tools(self):
self.lssrc_cmd = self.module.get_bin_path(‘lssrc’, True)

if not self.lssrc_cmd:
self.module.fail_json(msg=‘unable to find lssrc binary’)

self.startsrc_cmd = self.module.get_bin_path(‘startsrc’, True)

if not self.startsrc_cmd:
self.module.fail_json(msg=‘unable to find startsrc binary’)

self.stopsrc_cmd = self.module.get_bin_path(‘stopsrc’, True)

if not self.stopsrc_cmd:
self.module.fail_json(msg=‘unable to find stopsrc binary’)

self.refresh_cmd = self.module.get_bin_path(‘refresh’, True)

if not self.refresh_cmd:
self.module.fail_json(msg=‘unable to find refresh binary’)

def get_service_status(self):
rc, stdout, stderr = self.execute_command(“%s -s %s” % (self.lssrc_cmd, self.name))
if rc == 1:
self.running = False
elif rc == 0:
self.running = True

def get_service_status(self):
status = self.get_aix_src_status()

Only ‘active’ is considered properly running. Everything else is off

or has some sort of problem.

if status == ‘active’:
self.running = True
else:
self.running = False

def get_aix_src_status(self):
rc, stdout, stderr = self.execute_command(“%s -s %s” % (self.lssrc_cmd, self.name))
if rc == 1:
if stderr:
self.module.fail_json(msg=stderr)
else:
self.module.fail_json(msg=stdout)

lines = stdout.rstrip(“\n”).split(“\n”)
status = lines[-1].split(" ")[-1]

status is one of: active, inoperative

return status

def service_control(self):
if self.action == ‘start’:
srccmd = self.startsrc_cmd
elif self.action == ‘stop’:
srccmd = self.stopsrc_cmd
elif self.action == ‘reload’:
srccmd = self.refresh_cmd
elif self.action == ‘restart’:
self.execute_command(“%s -s %s” % (self.stopsrc_cmd, self.name))
srccmd = self.startsrc_cmd

if self.arguments and self.action == ‘start’:
return self.execute_command(“%s -a "%s" -s %s” % (srccmd, self.arguments, self.name))
else:
return self.execute_command(“%s -s %s” % (srccmd, self.name))

Happy to have AIX things added.

Can you please submit a github pull request instead?

Just submitted two pull requests for both changes.

My man Martin! Excellent!

See you on Friday!