parse_ignition.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. """Ansible action plugin to decode ignition payloads"""
  2. import base64
  3. import os
  4. import six
  5. from six.moves import urllib
  6. from ansible.plugins.action import ActionBase
  7. # pylint: disable=too-many-function-args
  8. def get_file_data(encoded_contents):
  9. """Decode data URLs as specified in RFC 2397"""
  10. # The following source is adapted from Python3 source
  11. # License: https://github.com/python/cpython/blob/3.7/LICENSE
  12. # retrieved from: https://github.com/python/cpython/blob/3.7/Lib/urllib/request.py
  13. _, data = encoded_contents.split(":", 1)
  14. mediatype, data = data.split(",", 1)
  15. # even base64 encoded data URLs might be quoted so unquote in any case:
  16. data = urllib.parse.unquote(data)
  17. if mediatype.endswith(";base64"):
  18. data = base64.b64decode(data).decode('utf-8')
  19. mediatype = mediatype[:-7]
  20. # End PSF software
  21. return data
  22. # pylint: disable=too-many-function-args
  23. def get_files(files_dict, systemd_dict, dir_list, data):
  24. """parse data to populate file_dict"""
  25. files = data.get('storage', []).get('files', [])
  26. for item in files:
  27. path = item["path"]
  28. dir_list.add(os.path.dirname(path))
  29. # remove prefix "data:,"
  30. encoded_contents = item['contents']['source']
  31. contents = get_file_data(encoded_contents)
  32. # convert from int to octal, padding at least to 4 places.
  33. # eg, 420 becomes '0644'
  34. mode = str(format(int(item["mode"]), '04o'))
  35. inode = {"contents": contents, "mode": mode}
  36. files_dict[path] = inode
  37. # get the systemd units files we're here
  38. systemd_units = data.get('systemd', []).get('units', [])
  39. for item in systemd_units:
  40. contents = item['contents']
  41. if six.PY2:
  42. # pylint: disable=redefined-variable-type
  43. contents = contents.decode('unicode-escape')
  44. mode = "0644"
  45. inode = {"contents": contents, "mode": mode}
  46. name = item['name']
  47. path = '/etc/systemd/system/' + name
  48. dir_list.add(os.path.dirname(path))
  49. files_dict[path] = inode
  50. enabled = item.get('enabled') or True
  51. systemd_dict[name] = enabled
  52. # pylint: disable=too-few-public-methods
  53. class ActionModule(ActionBase):
  54. """ActionModule for parse_ignition.py"""
  55. def run(self, tmp=None, task_vars=None):
  56. """Run parse_ignition action plugin"""
  57. result = super(ActionModule, self).run(tmp, task_vars)
  58. result["changed"] = False
  59. result["failed"] = False
  60. result["msg"] = "Parsed successfully"
  61. files_dict = {}
  62. systemd_dict = {}
  63. dir_list = set()
  64. result["files_dict"] = files_dict
  65. result["systemd_dict"] = systemd_dict
  66. # self.task_vars holds all in-scope variables.
  67. # Ignore settting self.task_vars outside of init.
  68. # pylint: disable=W0201
  69. self.task_vars = task_vars or {}
  70. ign_file_contents = self._task.args.get('ign_file_contents')
  71. get_files(files_dict, systemd_dict, dir_list, ign_file_contents)
  72. result["dir_list"] = list(dir_list)
  73. return result