efc953c43e5e9ac8bb360eb7a3e53281b04471d9
[bottlenecks.git] / vstf / vstf / common / saltstack.py
1 #!/usr/bin/env python
2 # coding=utf-8
3 import os
4 import sys
5 import inspect
6 import logging
7 import salt.client as sclient
8
9 from vstf.common import cmds
10
11 log = logging.getLogger(__name__)
12
13
14 class Mysalt(object):
15     IS_DIR = 1
16     IS_FILE = 2
17     FAILED = -1
18
19     def __init__(self):
20         self.cur_path = os.path.abspath(os.path.dirname(inspect.stack()[1][1]))
21         self.salt_conf = "/etc/salt/master"
22         if not os.path.exists(self.salt_conf):
23             raise Exception("this python must be run on the salt master.")
24         self.pillar_path = str(
25             cmds.execute("grep '^pillar_roots' \
26                     /etc/salt/master -A 2 | sed 1,2d | awk '{print $2}'") + '/')
27         if self.pillar_path == "":
28             log.warning("pillar path not found, make sure the pillar_roots configed")
29         else:
30             os.system("mkdir -p " + self.pillar_path)
31
32         self.state_path = str(cmds.execute("grep '^file_roots' \
33             /etc/salt/master -A 2 | sed 1,2d | awk '{print $2}'") + '/')
34         if self.state_path == "":
35             log.warning("state path not found, make sure the file_roots configed")
36         else:
37             os.system("mkdir -p " + self.state_path)
38
39         self.salt = sclient.LocalClient()
40
41     def slave_exists(self, host):
42         pslave = "/etc/salt/pki/master/minions/" + host
43         if os.path.exists(pslave):
44             return True
45         else:
46             return False
47
48     def __is_dir_or_file(self, src):
49         if not os.path.exists(src):
50             return self.FAILED
51         if os.path.isdir(src):
52             return self.IS_DIR
53         elif os.path.isfile(src):
54             return self.IS_FILE
55         else:
56             return self.FAILED
57
58     def __copy_target(self, target, flag=""):
59         if not os.path.exists(target):
60             log.error("target %(d)s  not exists.", {'d': target})
61             return False
62
63         if flag == "pillar":
64             dst = self.pillar_path
65         elif flag == "state":
66             dst = self.state_path
67         else:
68             log.error("this file or dir not pillar or state, can not support now.")
69             return False
70
71         if self.IS_FILE == self.__is_dir_or_file(target):
72             os.system('cp ' + target + ' ' + dst)
73         else:
74             os.system("cp -r " + target + ' ' + dst)
75         return True
76
77     def copy(self, host, src, dst):
78         """copy file or dir to slave.
79         :src a file or a dir
80         :dst if src is a file, the dst must be like this /home/xx.py, not /home
81         """
82
83         '''check if the host exists on the master'''
84         if not self.slave_exists(host):
85             log.error("the host %(h)s is not held by master, please check.")
86             return False
87
88         '''copy file to salt's file_roots'''
89         if not self.__copy_target(src, "state"):
90             return False
91
92         if self.IS_DIR == self.__is_dir_or_file(src):
93             dir_name = os.path.basename(src)
94             self.salt.cmd(host, "cp.get_dir", ["salt://" + dir_name, dst])
95         elif self.IS_FILE == self.__is_dir_or_file(src):
96             file_name = os.path.basename(src)
97             print self.salt.cmd(host, "cp.get_file", ["salt://" + file_name, dst])
98         else:
99             log.error("not file and not dir, what is it")
100             return False
101         return True
102
103     def __luxuriant_line(self, str, color):
104         if "red" == color:
105             return "\033[22;35;40m" + str + "\033[0m"
106         elif "green" == color:
107             return "\033[22;32;40m" + str + "\033[0m"
108         else:
109             return str
110
111     def result_check(self, ret, host):
112         num_s = 0
113         num_f = 0
114         msg = ""
115         try:
116             for key in ret[host].keys():
117                 if True == ret[host][key]['result']:
118                     num_s += 1
119                 else:
120                     num_f += 1
121                     msg = msg + self.__luxuriant_line("Failed %d:\n" % num_f, "red")
122                     msg = msg + "\t" + key + '\n'
123                     msg = msg + self.__luxuriant_line("\t%s\n" % ret[host][key]['comment'], "red")
124                     if True == ret[host][key]['changes'].has_key('retcode'):
125                         msg = msg + "RETCODE: %s\n" % (ret[host][key]['changes']['retcode'])
126                     if True == ret[host][key]['changes'].has_key('stderr'):
127                         msg = msg + "STDERR: %s\n" % (ret[host][key]['changes']['stderr'])
128                     if True == ret[host][key]['changes'].has_key('stdout'):
129                         msg = msg + "STDOUT: %s\n" % (ret[host][key]['changes']['stdout'])
130             msg = msg + self.__luxuriant_line("total success: %d\n" % num_s, "green")
131             msg = msg + self.__luxuriant_line("failed: %d\n" % num_f, "red")
132         except Exception as e:
133             log.error("sorry, thy to check result happend error, <%(e)s>.\nret:%(ret)s",
134                       {'e': e, 'ret': ret})
135             return -1
136         log.info(':\n' + msg)
137         return num_f
138
139     def run_state(self, host, fstate, ext_pillar={}, care_result=True):
140         try:
141             log.info("salt " + host + " state.sls " +
142                      fstate + ' pillar=\'' + str(ext_pillar) + '\'')
143             ret = self.salt.cmd(host, 'state.sls', [fstate, 'pillar=' + str(ext_pillar)], 180, 'list')
144         except Exception as e:
145             log.error("try to init host %(host)s happend error: <%(e)s>.",
146                       {'host': host, 'e': e})
147             if True == care_result:
148                 raise e
149
150         if 0 != self.result_check(ret, host) and care_result:
151             sys.exit(-1)
152         return True
153
154     def salt_cmd(self, host, cmd):
155         # import pdb
156         # pdb.set_trace()
157         logging.info("Begin to run cmd %s on %s" % (host, cmd))
158
159         try:
160             ret = self.salt.cmd(host, 'cmd.run', [cmd])
161         except Exception:
162             log.error("Remote salt execute failed.")
163         return ret
164
165     def copy_by_state(self, host, src, state_cmd, **kwargs):
166         '''the src must be a dir, and the state.sls 
167         must be the name of the dir name'''
168
169         if not self.slave_exists(host):
170             log.error("the host %(h)s is not held by master, please check.")
171             return False
172
173         if not self.__copy_target(src, "state"):
174             return False
175
176         return self.run_state(host, state_cmd, kwargs, care_result=True)
177
178     def get_master_ip(self, host=None):
179         if not host:
180             ret = cmds.execute("grep '^interface:' /etc/salt/master | awk '{print $2}'").strip()
181             return ret
182         try:
183             ret = self.salt.cmd(host, "grains.item", ["master"])[host]['master']
184         except Exception:
185             log.error("salt happened error when get master ip")
186             return ""
187         return ret
188
189
190 mysalt = Mysalt()