From b5f98ede42ed322aafc4395e74703ceda87b11c6 Mon Sep 17 00:00:00 2001 From: Viktor Tikkanen Date: Thu, 9 May 2019 11:30:35 +0300 Subject: [PATCH] Add seed code for ansible-role-ntp Change-Id: I52ba682ff1872170bf89e2bf638a29cf2ca53359 Signed-off-by: Viktor Tikkanen --- .gitreview | 5 + 0001-initial.patch | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 202 +++++++++++++++++ ansible-role-ntp.spec | 50 +++++ 4 files changed, 869 insertions(+) create mode 100644 .gitreview create mode 100644 0001-initial.patch create mode 100644 LICENSE create mode 100644 ansible-role-ntp.spec diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..27a4f98 --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.akraino.org +port=29418 +project=ta/ansible-role-ntp +defaultremote=origin diff --git a/0001-initial.patch b/0001-initial.patch new file mode 100644 index 0000000..1910607 --- /dev/null +++ b/0001-initial.patch @@ -0,0 +1,612 @@ +diff --git a/handlers/main.yml b/handlers/main.yml +index d18df56..6b58a86 100644 +--- a/handlers/main.yml ++++ b/handlers/main.yml +@@ -1,3 +1,14 @@ + --- ++- name: add ntp keys ++ ntp: ++ auth_type: "{{ time.auth_type }}" ++ ntpservers: "{{ ntp_config_server }}" ++ hosts: "{{ hosts }}" ++ filepath: "{{ time.serverkeys_path }}" ++ ++- name: create redundant fallback ntp servers ++ fallback_ntp_servers: ++ hosts: "{{ hosts }}" ++ + - name: restart ntp + service: name={{ ntp_service_name }} state=restarted +diff --git a/library/fallback_ntp_servers.py b/library/fallback_ntp_servers.py +new file mode 100644 +index 0000000..7d7907e +--- /dev/null ++++ b/library/fallback_ntp_servers.py +@@ -0,0 +1,73 @@ ++#!/bin/python ++# Copyright (C) 2017 Nokia ++# All rights reserved ++ ++from ansible.module_utils.basic import AnsibleModule ++import socket ++ ++DOCUMENTATION = ''' ++module: fallback_ntp_servers ++short_description: adding peers and fallback option ++description: Adding peers and an optional fallback option to the controllers. ++author: nokia.com ++options: ++ option-name: hosts ++ description: a list of controller hostnames ++ type: list ++''' ++ ++def get_hostname(): ++ return socket.gethostname().split('.')[0] ++ ++def get_controllers(hosts): ++ controllers = [] ++ for item in hosts: ++ if "controller" in hosts[item]['service_profiles']: ++ controllers.append(item) ++ return controllers ++ ++def is_installation_host(hosts): ++ hostname = get_hostname() ++ try: ++ if hosts[hostname]['installation_host']: ++ return True ++ except KeyError: ++ return False ++ return False ++ ++def add_peers(hosts): ++ hosts.remove(get_hostname()) ++ with open("/etc/ntp.conf") as cnf: ++ content = cnf.readlines() ++ for index, line in enumerate(content): ++ if "server" in line: ++ for peer in hosts: ++ content.insert(index, "peer %s\n" % peer) ++ break ++ with open("/etc/ntp.conf", "w") as cnf: ++ for line in content: ++ cnf.write(line) ++ ++def add_fallback(hosts): ++ if is_installation_host(hosts): ++ with open("/etc/ntp.conf") as cnf: ++ content = cnf.readlines() ++ for index, line in enumerate(content): ++ if "peer" in line: ++ content.insert(index, "fudge 127.127.1.0 stratum 10\n") ++ break ++ with open("/etc/ntp.conf", "w") as cnf: ++ for line in content: ++ cnf.write(line) ++ ++def main(): ++ module_args = dict(hosts=dict(type='dict', required=True)) ++ module = AnsibleModule(argument_spec=module_args) ++ controllers = get_controllers(module.params['hosts']) ++ if get_hostname() in controllers: ++ add_peers(controllers) ++ add_fallback(module.params['hosts']) ++ module.exit_json(msg="configured") ++ ++if __name__ == "__main__": ++ main() +diff --git a/library/ntp.py b/library/ntp.py +new file mode 100644 +index 0000000..cef833e +--- /dev/null ++++ b/library/ntp.py +@@ -0,0 +1,476 @@ ++#!/bin/python ++# Copyright (C) 2018 Nokia ++# All rights reserved ++ ++import re ++import os ++from subprocess import check_output, CalledProcessError ++from ansible.module_utils.basic import AnsibleModule ++import socket ++import yaml ++import random ++import string ++import requests ++from urlparse import urlparse ++ ++DOCUMENTATION = ''' ++module: ntp ++short_description: configuring authentication ++description: Configuring the authentication of NTP servers. ++author: nokia.com ++options: ++ option-name: auth_type ++ description: the authentication type used by the server ++ type: string ++ choices: crypto, symmetric, none ++ default: crypto ++ option-name: hosts ++ description: a list of the controllers, in order to decide if the script is executed on controller or compute host ++ type: list ++ option-name: ntpservers ++ description: a list the NTP servers ++ type: list ++ option-name: filepath ++ description: the url of the required keys ++ type: string ++ default: empty string ++''' ++ ++def get_hostname(): ++ return socket.gethostname().split('.')[0] ++ ++def get_controllers(hosts): ++ controllers = [] ++ for item in hosts: ++ if "controller" in hosts[item]['service_profiles']: ++ controllers.append(item) ++ return controllers ++ ++crypto_keys_dir = "/etc/ntp/crypto" ++crypto_parameter_file = crypto_keys_dir + "/params" ++symmetric_key_file = "/etc/ntp/keys" ++ ++class KeyAuthDisabled(Exception): ++ pass ++ ++class SymmetricKeyNotFound(Exception): ++ pass ++ ++class NtpCryptoKeyHandler(object): ++ supported_types = {"iff": "ntpkey_iffkey_", "mv": "ntpkey_mvta_", "mvta": "ntpkey_mvta_", "gq": "ntpkey_gqkey_"} ++ ++ def del_key(self, server, type): ++ filename = "%s/%s" % (crypto_keys_dir, self._get_filename(type, server)) ++ os.remove(filename) ++ with open("/etc/ntp.conf") as cnf: ++ content = cnf.readlines() ++ regex = re.compile("\Aserver\s+%s\s+autokey" % server) ++ for index, line in enumerate(content): ++ if re.match(regex, line) is not None: ++ content.pop(index) ++ content.insert(index, "server %s\n" % server) ++ with open("/etc/ntp.conf", "w") as cnf: ++ for line in content: ++ cnf.write(line) ++ ++ def add_key(self, servers): ++ self._remove_symmetric_keys() ++ self._enable_crypto_auth() ++ self._copy_keys(servers) ++ self._remove_old_client_keys(servers) ++ self._create_client_key() ++ self.set_key_permissions() ++ ++ def update_client_certificate(self): ++ passwd = self._create_client_password() ++ os.system("cd %s; ntp-keygen -q %s" % (crypto_keys_dir, passwd)) ++ ++ def _enable_crypto_auth(self): ++ self._create_client_password() ++ with open("/etc/ntp.conf") as cnf: ++ content = cnf.readlines() ++ includefile_is_correct = False ++ keysdir_is_correct = False ++ for index, line in enumerate(content): ++ if line.startswith("crypto"): ++ content.pop(index) ++ elif line.startswith("includefile"): ++ self.replace_line_in_ntpconf(line, content, index, "includefile", crypto_parameter_file) ++ includefile_is_correct = True ++ elif line.startswith("keysdir"): ++ self.replace_line_in_ntpconf(line, content, index, "keysdir", crypto_keys_dir) ++ keysdir_is_correct = True ++ if not includefile_is_correct: ++ content.append("includefile %s\n" % crypto_parameter_file) ++ if not keysdir_is_correct: ++ content.append("keysdir %s\n" % crypto_keys_dir) ++ with open("/etc/ntp.conf", "w") as cnf: ++ for line in content: ++ cnf.write(line) ++ ++ def replace_line_in_ntpconf(self, line, filecontent, index, linestarting, lineparameter): ++ if line.split()[1] != lineparameter: ++ filecontent.pop(index) ++ filecontent.insert(index, "%s %s\n" % (linestarting, lineparameter)) ++ ++ def _create_client_password(self): ++ if not os.path.exists(crypto_parameter_file): ++ os.mknod(crypto_parameter_file) ++ with open(crypto_parameter_file) as param: ++ content = param.readlines() ++ for line in content: ++ match = re.match(re.compile("\Acrypto\s+pw\s+"), line) ++ if match is not None: ++ return line.split(None, 2)[2] ++ randstr = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(32)]) ++ content.append("crypto pw %s\n" % randstr) ++ with open(crypto_parameter_file, "w") as param: ++ for line in content: ++ param.write(line) ++ return randstr ++ ++ def _remove_symmetric_keys(self): ++ with open(symmetric_key_file, "w") as obj: ++ obj.truncate() ++ ++ def _remove_old_client_keys(self, servers): ++ present_files = os.listdir(crypto_keys_dir) ++ possible_needed_files = [os.path.basename(crypto_parameter_file), os.path.basename(symmetric_key_file)] ++ for srv in servers: ++ possible_needed_files = possible_needed_files + self._get_all_supported_filenames(srv['server']) ++ for f in possible_needed_files: ++ try: ++ present_files.remove(f) ++ except ValueError: ++ pass ++ for f in present_files: ++ os.remove("%s/%s" % (crypto_keys_dir, f)) ++ ++ def _create_client_key(self): ++ clientpassword = self._create_client_password() ++ os.system("cd %s; ntp-keygen -H -c RSA-SHA1 -p %s" % (crypto_keys_dir, clientpassword)) ++ ++ def _copy_keys(self, servers): ++ print "copying keys" ++ for srv in servers: ++ self._remove_old_versions_of_key(str(srv["server"])) ++ filename = self._get_filename(str(srv["key"]["type"]), str(srv["server"])) ++ if not os.path.exists(filename): ++ os.mknod(filename) ++ with open("%s/%s" % (crypto_keys_dir, filename), "w") as keyfile: ++ for key in srv["key"]["keys"]: ++ keyfile.write("-----BEGIN ENCRYPTED PRIVATE KEY-----\n") ++ keyfile.write("%s\n" % key) ++ keyfile.write("-----END ENCRYPTED PRIVATE KEY-----\n") ++ ++ def _get_filename(self, type, server): ++ filename = "%s%s" % (NtpCryptoKeyHandler.supported_types[type], server) ++ return filename ++ ++ def _get_all_supported_filenames(self, server): ++ filenames = [] ++ for type in NtpCryptoKeyHandler.supported_types: ++ filenames.append(self._get_filename(type, server)) ++ return filenames ++ ++ def _remove_old_versions_of_key(self, server): ++ possible_filenames = self._get_all_supported_filenames(server) ++ for key in possible_filenames: ++ try: ++ os.remove("%s/%s" % (crypto_keys_dir, key)) ++ except OSError: ++ pass ++ ++ def set_key_permissions(self): ++ dircontent = os.listdir(crypto_keys_dir) ++ for f in dircontent: ++ os.chmod("%s/%s" % (crypto_keys_dir, f), 0600) ++ ++ ++class NtpSymmetricKeyHandler(object): ++ ++ def add_key(self, key, server): ++ keys_file = symmetric_key_file ++ if not self._is_symmetric_key_auth_enabled(): ++ self.enable_symmetric_key_auth(keys_file) ++ else: ++ keys_file = self._get_symmetric_key_file() ++ try: ++ key_id = self._get_symmetric_key_id(key, keys_file) ++ except SymmetricKeyNotFound: ++ key_id = self._find_highest_id(keys_file) + 1 ++ with open(keys_file, "a") as keys: ++ keys.write("# %s\n" % server) ++ keys.write("%s M %s\n" % (key_id, key)) ++ self._add_trustedkey(key_id) ++ self._add_controlkey(key_id) ++ self._add_requestkey(key_id) ++ ++ def _enable_key_in_ntpconf(self, old, key_id): ++ is_replaced = False ++ key_id = str(key_id) ++ with open("/etc/ntp.conf", "r") as file: ++ buff = file.readlines() ++ for index, line in enumerate(buff): ++ if (line.startswith(old)) and (key_id not in line): ++ buff[index] = line.rstrip('\n') + " " + str(key_id) + "\n" ++ is_replaced = True ++ break ++ elif (line.startswith(old)) and (key_id in line): ++ is_replaced = True ++ break ++ if is_replaced: ++ with open("/etc/ntp.conf", "w") as file: ++ for line in buff: ++ file.write(line) ++ else: ++ with open("/etc/ntp.conf", "a") as file: ++ file.write("%s %s\n" % (old, str(key_id))) ++ ++ def _add_trustedkey(self, key_id): ++ self._enable_key_in_ntpconf("trustedkey", str(key_id)) ++ ++ def _add_controlkey(self, key_id): ++ self._enable_key_in_ntpconf("controlkey", str(key_id)) ++ ++ def _add_requestkey(self, key_id): ++ self._enable_key_in_ntpconf("requestkey", str(key_id)) ++ ++ def _find_highest_id(self, keys_file): ++ ids = [] ++ with open(keys_file) as keys: ++ for o in keys.readlines(): ++ id = re.findall("^[0-9]+", o) ++ if len(id) > 0: ++ ids.append(int(id[0])) ++ ids.sort() ++ if len(ids) != 0: ++ return ids[-1] ++ else: ++ return 0 ++ ++ def _is_symmetric_key_auth_enabled(self): ++ with open("/etc/ntp.conf") as cnf: ++ for line in cnf.read().split('\n'): ++ if line.startswith("keys"): ++ return True ++ return False ++ ++ def _get_symmetric_key_file(self): ++ with open("/etc/ntp.conf") as cnf: ++ for line in cnf.read().split('\n'): ++ if "keys" in line: ++ return line.split()[1] ++ raise KeyAuthDisabled() ++ ++ def _get_symmetric_key_id(self, key, keysfile=symmetric_key_file): ++ with open(keysfile) as keys: ++ for line in keys.read().split('\n'): ++ if key in line: ++ return line.split()[0] ++ raise SymmetricKeyNotFound() ++ ++ def enable_symmetric_key_auth(self, keys_loc=symmetric_key_file): ++ try: ++ self._get_symmetric_key_file() ++ except KeyAuthDisabled: ++ with open("/etc/ntp.conf", "a") as cnf: ++ cnf.write("keys %s\n" % keys_loc) ++ ++ ++class NtpServerHandler(object): ++ ++ def __init__(self, auth_type): ++ if auth_type == "symmetric": ++ self.keyhandler = NtpSymmetricKeyHandler() ++ else: ++ self.keyhandler = NtpCryptoKeyHandler() ++ self.auth_type = auth_type ++ ++ def delete_other_keys(self): ++ if os.path.exists(crypto_keys_dir): ++ dircontent = os.listdir(crypto_keys_dir) ++ for f in dircontent: ++ os.remove("%s/%s" % (crypto_keys_dir, f)) ++ ++ def delete_server(self, server): ++ with open("/etc/ntp.conf") as conf: ++ contents = conf.readlines() ++ for index, line in enumerate(contents): ++ if server in line: ++ contents.pop(index) ++ break ++ with open("/etc/ntp.conf", "w") as conf: ++ for line in contents: ++ conf.write(line) ++ self._restart_ntpd() ++ ++ def add_server(self, servers): ++ self.delete_other_keys() ++ for srv in servers: ++ if self.auth_type != "none": ++ if self.auth_type == "symmetric": ++ self.keyhandler.add_key(srv['key'], srv['server']) ++ else: ++ self.keyhandler.add_key(servers) ++ self._insert_server_to_config(srv['server'], self.auth_type, srv['key']) ++ ++ def _restart_ntpd(self): ++ os.system("systemctl restart ntpd") ++ ++ def _insert_server_to_config(self, server, auth_type, key=None): ++ if auth_type == "symmetric": ++ keyfile = self.keyhandler._get_symmetric_key_file() ++ id = self.keyhandler._get_symmetric_key_id(key, keyfile) ++ serverline = "server " + server + " key " + str(id) + '\n' ++ elif auth_type == "crypto": ++ serverline = "server " + server + " autokey\n" ++ else: ++ serverline = "server " + server + '\n' ++ with open("/etc/ntp.conf") as cnf: ++ contents = cnf.readlines() ++ server_was_found = False ++ for index, line in enumerate(contents): ++ if (server in line) and (line.startswith("server")): ++ contents[index] = serverline ++ server_was_found = True ++ break ++ if not server_was_found: ++ index = 0 ++ for line in contents: ++ if line.startswith("server"): ++ break ++ index += 1 ++ contents.insert(index, serverline) ++ with open("/etc/ntp.conf", "w") as cnf: ++ for line in contents: ++ cnf.write(line) ++ ++ def get_ntp_status(self): ++ try: ++ print check_output(["systemctl", "status", "ntpd"]) ++ except CalledProcessError as e: ++ print e.output ++ try: ++ print check_output(["ntpstat", "-u"]) ++ except CalledProcessError as e: ++ print e.output ++ try: ++ print check_output(["ntpq", "-c", "as"]) ++ except CalledProcessError as e: ++ print e.output ++ ++def find_old_crypto_keys(remaining_servers): ++ files = os.listdir(crypto_keys_dir) ++ found_keys = [] ++ for f in files: ++ for s in remaining_servers: ++ if 'ntpkey_' in f and s in f: ++ with open("%s/%s" % (crypto_keys_dir, f)) as keyfcnt: ++ content = keyfcnt.readlines() ++ regex = re.compile("-----BEGIN ENCRYPTED PRIVATE KEY-----\n|-----END ENCRYPTED PRIVATE KEY-----\n") ++ keycont = ''.join(content) ++ raw_keys = re.split(regex, keycont) ++ raw_keys = filter(None, raw_keys) ++ if f == 'ntpkey_iffkey_%s' % s: ++ type = 'iff' ++ elif f == 'ntpkey_gqkey_%s' % s: ++ type = 'gq' ++ elif f == 'ntpkey_mvta_%s' % s: ++ type = 'mv' ++ else: ++ raise Exception("Something is wrong with the filename %s/%s" % (crypto_keys_dir, f)) ++ found_keys.append({'server': s, 'key': {'type': type, 'keys': raw_keys}}) ++ return found_keys ++ ++ ++ ++def find_old_symmetric_keys(remaining_servers): ++ keyfile = NtpSymmetricKeyHandler()._get_symmetric_key_file() ++ retlist = [] ++ with open(keyfile) as kf: ++ content = kf.readlines() ++ for srv in remaining_servers: ++ if "# %s" % srv in content: ++ try: ++ index = content.index("# %s" % srv) ++ key = content[index + 1].split()[2] ++ retlist.append({"server": srv, "key": key}) ++ except ValueError: ++ pass ++ return retlist ++ ++ ++def get_ntp_servers(url, ntpservers, auth_type): ++ file_reachable = True ++ servers = [] ++ if url.startswith("file://"): ++ path = url.lstrip("file://") ++ try: ++ with open(path) as f: ++ f_content = f.read() ++ except IOError: ++ file_reachable = False ++ else: ++ try: ++ r = requests.get(url) ++ if r.status_code != 200: ++ raise requests.exceptions.ConnectionError() ++ f_content = r.content ++ except requests.exceptions.ConnectionError: ++ file_reachable = False ++ if file_reachable: ++ yaml_content = yaml.load(f_content) ++ for item in yaml_content: ++ srv = item.keys()[0] ++ if srv in ntpservers: ++ element = {"server": srv, "key": item[srv]} ++ servers.append(element) ++ found_servers = [item['server'] for item in servers] ++ if len(found_servers) != len(ntpservers): ++ remaining_servers = [item for item in ntpservers if item not in found_servers] ++ if auth_type == "crypto": ++ leftover_servers = find_old_crypto_keys(remaining_servers) ++ elif auth_type == "symmetric": ++ leftover_servers = find_old_symmetric_keys(remaining_servers) ++ else: ++ raise Exception("Unknown authentication type for NTP!") ++ servers = servers + leftover_servers ++ if len(servers) != len(ntpservers): ++ raise Exception("Something must be messed up in your config. The NTP servers provided by your key file and by your configuration doesn't match!") ++ return servers ++ ++ ++def remove(url): ++ o = urlparse(url) ++ if o.scheme == "file": ++ os.remove(o.path) ++ ++ ++ ++def main(): ++ module_args = dict(auth_type=dict(type='str', required=True), ++ hosts=dict(type='dict', required=True), ++ ntpservers=dict(type='list', required=True), ++ filepath=dict(type='str', required=True)) ++ module = AnsibleModule(argument_spec=module_args) ++ controllers = get_controllers(module.params['hosts']) ++ hostname = get_hostname() ++ if hostname not in controllers: ++ remove(module.params['filepath']) ++ module.exit_json(msg="nothing to do; the script is not executed on a controller host") ++ return 0 ++ if module.params['auth_type'] == "symmetric" or module.params['auth_type'] == "crypto": ++ servers = get_ntp_servers(module.params['filepath'], module.params['ntpservers'], module.params['auth_type']) ++ handler = NtpServerHandler(module.params['auth_type']) ++ handler.add_server(servers) ++ elif module.params['auth_type'] == "none": ++ pass ++ else: ++ raise Exception("Invalid authentication type: %s" % module.params['auth_type']) ++ remove(module.params['filepath'].rstrip("file://")) ++ module.exit_json(msg="all done") ++ ++if __name__ == "__main__": ++ main() ++ +diff --git a/tasks/main.yml b/tasks/main.yml +index 5c80a4a..cc4f4b6 100644 +--- a/tasks/main.yml ++++ b/tasks/main.yml +@@ -1,4 +1,6 @@ + --- ++- shell: file=/etc/sysconfig/ntpdate; sed -i 's/SYNC_HWCLOCK=no/SYNC_HWCLOCK=yes/g' ${file};grep "SYNC_HWCLOCK=yes" ${file} || echo "SYNC_HWCLOCK=yes" >> ${file} ++ + - name: Add the OS specific variables + include_vars: '{{ ansible_os_family }}.yml' + tags: [ 'configuration', 'package', 'service', 'ntp' ] +@@ -16,9 +18,20 @@ + - name: Copy the ntp.conf template file + template: src=ntp.conf.j2 dest=/etc/ntp.conf + notify: ++ - add ntp keys ++ - create redundant fallback ntp servers + - restart ntp + tags: [ 'configuration', 'package', 'ntp' ] + ++- name: Clear step-tickers ++ lineinfile: ++ dest: /etc/ntp/step-tickers ++ regexp: ".*" ++ state: absent ++ ++- name: enable ntpdate at boot time ++ shell: chkconfig ntpdate on ++ + - name: Start/stop ntp service + service: name={{ ntp_service_name }} state={{ ntp_service_state }} enabled={{ ntp_service_enabled }} pattern='/ntpd' + tags: [ 'service', 'ntp' ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ansible-role-ntp.spec b/ansible-role-ntp.spec new file mode 100644 index 0000000..f0ec3cc --- /dev/null +++ b/ansible-role-ntp.spec @@ -0,0 +1,50 @@ +# Copyright 2019 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Name: ansible-role-ntp +Version: 0.4.0.2.g4fc673c +Release: 1%{?dist} +Summary: This role enables users to install and configure ntp on their hosts. + +Vendor: %{_platform_vendor} and Benno Joy, René Moser modified +License: %{_platform_licence} and BSD +URL: https://github.com/resmo/ansible-role-ntp +Source0: https://github.com/resmo/%{name}/archive/%{sha}.zip +Patch0: 0001-initial.patch + +BuildArch: noarch +BuildRequires: python python-setuptools +Requires: ansible => 1.4 + +%global sha 4fc673cfdf6cb3704a8be10cd10728a21be8f1bb + +%description + +%prep +%autosetup -n %{name}-%{sha} -p 1 + +%install +mkdir -p %{buildroot}/etc/ansible/roles/ntp + +rsync -av --exclude ansible-role-ntp.spec \ + --exclude .travis.yml \ + --exclude .gitignore \ + --exclude README.md \ + --exclude *tests* \ + --exclude .git \ + --exclude .gitreview \ + . %{buildroot}/etc/ansible/roles/ntp/ +%files +%defattr(0755,root,root) +/etc/ansible/roles/ntp/ -- 2.16.6