load_user_files.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/python
  2. class LoadVarDir(object):
  3. def __init__(self, module):
  4. self.module = module
  5. self.path = self.module.params["path"]
  6. self.fact = self.module.params["fact"]
  7. self.data_bag = self.module.params["databag"]
  8. self.extract_extra_keys = self.module.params["extract_extra_keys"]
  9. # Chef => Ansible mapping
  10. self.mapping = {"comment": "comment",
  11. "force": "force",
  12. "gid": "group",
  13. "groups": "groups",
  14. "home": "home",
  15. "manage_home": "move_home",
  16. "non_unique": "non_unique",
  17. "password": "password",
  18. "shell": "shell",
  19. "action": "state",
  20. "system": "system",
  21. "uid": "uid",
  22. "ssh_keys": "keys"
  23. }
  24. self.file_data = {}
  25. def parse_yaml(self, data, path_hint=None):
  26. ''' convert a yaml string to a data structure. Also supports JSON, ssssssh!!!'''
  27. stripped_data = data.lstrip()
  28. loaded = None
  29. if stripped_data.startswith("{") or stripped_data.startswith("["):
  30. # since the line starts with { or [ we can infer this is a JSON document.
  31. try:
  32. loaded = json.loads(data)
  33. except ValueError as ve:
  34. if path_hint:
  35. self.module.fail_json(msg=path_hint + ": " + str(ve))
  36. else:
  37. self.module.fail_json(msg=str(ve))
  38. else:
  39. # else this is pretty sure to be a YAML document
  40. loaded = yaml.load(data, Loader=Loader)
  41. return loaded
  42. def parse_yaml_from_file(self, path, vault_password=None):
  43. ''' convert a yaml file to a data structure '''
  44. data = None
  45. try:
  46. data = open(path).read()
  47. except IOError:
  48. self.module.fail_json(msg="file could not read: %s" % path)
  49. try:
  50. return self.parse_yaml(data, path_hint=path)
  51. except yaml.YAMLError as exc:
  52. self.module.fail_json(msg="Syntax error in yaml file '%s'" % path)
  53. def main(self):
  54. self._check_variable()
  55. result = {"changed": False, "msg": "Hi", self.fact: self.file_data}
  56. self.module.exit_json(**result)
  57. def _read_from_file(self, file_path, databag):
  58. data = self.parse_yaml_from_file(file_path, vault_password="")
  59. if data and type(data) != dict:
  60. self.module.fail_json(msg="%s must be stored as a dictionary/hash".format(file_path))
  61. elif data is None:
  62. data = {}
  63. if databag:
  64. data = self.convert_chef_user_data_bag(data)
  65. return data
  66. def convert_chef_user_data_bag(self, data):
  67. print(("data=", data))
  68. if len(data) == 0:
  69. return data
  70. else:
  71. new_data = {}
  72. user_name = data.pop("id") # Should fail if no id
  73. # Loop and only pick item in our map and translate it to ansible ignore the rest
  74. for mapping_key in self.mapping:
  75. data_bag_item_value = data.pop(mapping_key, None)
  76. if data_bag_item_value:
  77. ansible_key = self.mapping.get(mapping_key)
  78. new_data.update({ansible_key: data_bag_item_value})
  79. if self.extract_extra_keys:
  80. for key, value in data.iteritems():
  81. new_data.update({key: value})
  82. # Check for an action
  83. chef_action = new_data.get("state", False)
  84. if chef_action:
  85. if chef_action == "create":
  86. new_data["state"] = "present"
  87. elif chef_action == "remove":
  88. new_data["state"] = "absent"
  89. chef_groups = new_data.get("groups", False)
  90. primary_group = new_data.get("group", False)
  91. if primary_group in chef_groups:
  92. # Databag issue for smart-os Issue
  93. chef_groups = [group_item for group_item in chef_groups if group_item != primary_group]
  94. new_data["groups"] = ",".join(chef_groups)
  95. return {user_name: new_data}
  96. def _check_variable(self):
  97. for path_item in self.path:
  98. try:
  99. path = path_item.get("path")
  100. path = os.path.expanduser(path)
  101. databag = path_item.get("databag", self.data_bag)
  102. #print "all={} type={} path={} databag={}".format(path_item, type(path_item), path, databag)
  103. except Exception as E:
  104. self.module.fail_json(msg="Path is a list but is malformed could not get 'path' got '{}'. Error '{}'".
  105. format(path_item, E))
  106. self._follow_path(path, databag)
  107. def _follow_path(self, path, databag):
  108. if os.path.exists(path):
  109. if os.path.isdir(path):
  110. for root, dirs, files in os.walk(path, topdown=False):
  111. for filename in files:
  112. self.file_data.update(self._read_from_file(path + "/" + filename, databag))
  113. else:
  114. self.file_data.update(self._read_from_file(path, databag))
  115. else:
  116. self.module.fail_json(msg="Failed to find path '{}'.".format(path))
  117. def main():
  118. module = AnsibleModule(
  119. argument_spec=dict(
  120. name=dict(default=None, aliases=["path"], required=True, type='list'),
  121. fact=dict(default="var_dir", required=False),
  122. databag=dict(default=False, type='bool'),
  123. extract_extra_keys=dict(default=True, required=False)
  124. ),
  125. supports_check_mode=False
  126. )
  127. if not ansible_client_found:
  128. module.fail_json(msg="Ansible is not installed or ansible python library is not in path. Can't import 'ansible.utils '")
  129. LoadVarDir(module).main()
  130. # import module snippets
  131. from ansible.module_utils.basic import *
  132. try:
  133. import yaml
  134. except ImportError:
  135. ansible_client_found = False
  136. else:
  137. ansible_client_found = True
  138. try:
  139. from yaml import CSafeLoader as Loader
  140. except ImportError:
  141. from yaml import SafeLoader as Loader
  142. main()