Initial patch with all code from CableLabs repository.
[snaps.git] / snaps / provisioning / ansible_utils.py
1 # Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16
17 from collections import namedtuple
18
19 import os
20 import paramiko
21
22 from ansible.parsing.dataloader import DataLoader
23 from ansible.vars import VariableManager
24 from ansible.inventory import Inventory
25 from ansible.executor.playbook_executor import PlaybookExecutor
26
27 __author__ = 'spisarski'
28
29 logger = logging.getLogger('ansible_utils')
30
31
32 def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path, variables=None, proxy_setting=None):
33     """
34     Executes an Ansible playbook to the given host
35     :param playbook_path: the (relative) path to the Ansible playbook
36     :param hosts_inv: a list of hostnames/ip addresses to which to apply the Ansible playbook
37     :param host_user: A user for the host instances (must be a password-less sudo user if playbook has "sudo: yes"
38     :param ssh_priv_key_file_path: the file location of the ssh key
39     :param variables: a dictionary containing any substitution variables needed by the Jinga 2 templates
40     :param proxy_setting: instance of os_credentials.ProxySettings class
41     :return: the results
42     """
43     if not os.path.isfile(playbook_path):
44         raise Exception('Requested playbook not found - ' + playbook_path)
45     if not os.path.isfile(ssh_priv_key_file_path):
46         raise Exception('Requested private SSH key not found - ' + ssh_priv_key_file_path)
47
48     import ansible.constants
49     ansible.constants.HOST_KEY_CHECKING = False
50
51     variable_manager = VariableManager()
52     if variables:
53         variable_manager.extra_vars = variables
54
55     loader = DataLoader()
56     inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=hosts_inv)
57     variable_manager.set_inventory(inventory)
58
59     ssh_extra_args = None
60     if proxy_setting and proxy_setting.ssh_proxy_cmd:
61         ssh_extra_args = '-o ProxyCommand=\'' + proxy_setting.ssh_proxy_cmd + '\''
62
63     options = namedtuple('Options', ['listtags', 'listtasks', 'listhosts', 'syntax', 'connection', 'module_path',
64                                      'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args',
65                                      'become', 'become_method', 'become_user', 'verbosity', 'check'])
66
67     ansible_opts = options(listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh',
68                            module_path=None, forks=100, remote_user=host_user, private_key_file=ssh_priv_key_file_path,
69                            ssh_common_args=None, ssh_extra_args=ssh_extra_args, become=None, become_method=None,
70                            become_user=None, verbosity=11111, check=False)
71
72     logger.debug('Setting up Ansible Playbook Executor for playbook - ' + playbook_path)
73     executor = PlaybookExecutor(
74         playbooks=[playbook_path],
75         inventory=inventory,
76         variable_manager=variable_manager,
77         loader=loader,
78         options=ansible_opts,
79         passwords=None)
80
81     logger.debug('Executing Ansible Playbook - ' + playbook_path)
82     retval = executor.run()
83
84     if retval != 0:
85         logger.error('Playbook application failed [' + playbook_path + '] with return value of - ' + str(retval))
86         raise Exception('Playbook not applied - ' + playbook_path)
87
88     return retval
89
90
91 def ssh_client(ip, user, private_key_filepath, proxy_settings=None):
92     """
93     Retrieves and attemts an SSH connection
94     :param ip: the IP of the host to connect
95     :param user: the user with which to connect
96     :param private_key_filepath: the path to the private key file
97     :param proxy_settings: instance of os_credentials.ProxySettings class (optional)
98     :return: the SSH client if can connect else false
99     """
100     logger.debug('Retrieving SSH client')
101     ssh = paramiko.SSHClient()
102     ssh.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
103
104     try:
105         proxy_cmd = None
106         if proxy_settings and proxy_settings.ssh_proxy_cmd:
107             proxy_cmd_str = str(proxy_settings.ssh_proxy_cmd.replace('%h', ip))
108             proxy_cmd_str = proxy_cmd_str.replace("%p", '22')
109             proxy_cmd = paramiko.ProxyCommand(proxy_cmd_str)
110
111         ssh.connect(ip, username=user, key_filename=private_key_filepath, sock=proxy_cmd)
112         return ssh
113     except Exception as e:
114         logger.warn('Unable to connect via SSH with message - ' + e.message)