Salome HOME
adapt status for apt
[tools/sat.git] / src / system.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 '''
20 In this file : all functions that do a system call, 
21 like open a browser or an editor, or call a git command
22 '''
23
24 import os
25 import subprocess
26 import time
27 import tarfile
28
29 import debug as DBG
30 import utilsSat as UTS
31 import src
32
33 from . import printcolors
34
35 def show_in_editor(editor, filePath, logger):
36     '''open filePath using editor.
37     
38     :param editor str: The editor to use.
39     :param filePath str: The path to the file to open.
40     '''
41     # default editor is vi
42     if editor is None or len(editor) == 0:
43         editor = 'vi'
44     
45     if '%s' not in editor:
46         editor += ' %s'
47
48     try:
49         # launch cmd using subprocess.Popen
50         cmd = editor % filePath
51         logger.write('Launched command:\n' + cmd + '\n', 5)
52         p = subprocess.Popen(cmd, shell=True)
53         p.communicate()
54     except:
55         logger.write(printcolors.printcError(_("Unable to edit file %s\n") 
56                                              % filePath), 1)
57
58 def git_describe(repo_path):
59     '''Use git describe --tags command to return tag description of the git repository"
60     :param repo_path str: The git repository to describe
61     '''
62     git_cmd="cd %s;git describe --tags" % repo_path
63     p = subprocess.Popen(git_cmd, shell=True,
64                     stdin=subprocess.PIPE,
65                     stdout=subprocess.PIPE,
66                     stderr=subprocess.PIPE)
67     p.wait()
68     if p.returncode != 0:
69         return False
70     else:
71         return p.stdout.readlines()[0].strip()
72
73
74 def git_extract(from_what, tag, where, logger, environment=None):
75   '''Extracts sources from a git repository.
76
77   :param from_what str: The remote git repository.
78   :param tag str: The tag.
79   :param where str: The path where to extract.
80   :param logger Logger: The logger instance to use.
81   :param environment src.environment.Environ: The environment to source when extracting.
82   :return: True if the extraction is successful
83   :rtype: boolean
84   '''
85   DBG.write("git_extract", [from_what, tag, str(where)])
86   if not where.exists():
87     where.make()
88   if tag == "master" or tag == "HEAD":
89     if src.architecture.is_windows():
90       cmd = "git clone %(remote)s %(where)s"
91     else:
92       cmd = r"""
93 set -x
94 git clone %(remote)s %(where)s
95 """
96     cmd = cmd % {'remote': from_what, 'tag': tag, 'where': str(where)}
97   else:
98     # NOTICE: this command only works with recent version of git
99     #         because --work-tree does not work with an absolute path
100     where_git = os.path.join(str(where), ".git")
101     if src.architecture.is_windows():
102       cmd = "rmdir %(where)s && git clone %(remote)s %(where)s && git --git-dir=%(where_git)s --work-tree=%(where)s checkout %(tag)s"
103     else:
104       cmd = r"""
105 set -x
106 rmdir %(where)s
107 git clone %(remote)s %(where)s && \
108 git --git-dir=%(where_git)s --work-tree=%(where)s checkout %(tag)s
109 """
110     cmd = cmd % {'remote': from_what,
111                  'tag': tag,
112                  'where': str(where),
113                  'where_git': where_git}
114
115
116   logger.logTxtFile.write("\n" + cmd + "\n")
117   logger.logTxtFile.flush()
118
119   DBG.write("cmd", cmd)
120
121   # git commands may fail sometimes for various raisons 
122   # (big module, network troubles, tuleap maintenance)
123   # therefore we give several tries
124   i_try = 0
125   max_number_of_tries = 3
126   sleep_delay = 30  # seconds
127   while (True):
128     i_try += 1
129     rc = UTS.Popen(cmd, cwd=str(where.dir()), env=environment.environ.environ, logger=logger)
130     if rc.isOk() or (i_try>=max_number_of_tries):
131       break
132     logger.write('\ngit command failed! Wait %d seconds and give an other try (%d/%d)\n' % \
133                  (sleep_delay, i_try + 1, max_number_of_tries), 3)
134     time.sleep(sleep_delay) # wait a little
135
136   return rc.isOk()
137
138
139 def git_extract_sub_dir(from_what, tag, where, sub_dir, logger, environment=None):
140   '''Extracts sources from a subtree sub_dir of a git repository.
141
142   :param from_what str: The remote git repository.
143   :param tag str: The tag.
144   :param where str: The path where to extract.
145   :param sub_dir str: The relative path of subtree to extract.
146   :param logger Logger: The logger instance to use.
147   :param environment src.environment.Environ: The environment to source when extracting.
148   :return: True if the extraction is successful
149   :rtype: boolean
150   '''
151   strWhere = str(where)
152   tmpWhere = strWhere + '_tmp'
153   parentWhere = os.path.dirname(strWhere)
154   if not os.path.exists(parentWhere):
155     logger.error("not existing directory: %s" % parentWhere)
156     return False
157   if os.path.isdir(strWhere):
158     logger.error("do not override existing directory: %s" % strWhere)
159     return False
160   aDict = {'remote': from_what,
161            'tag': tag,
162            'sub_dir': sub_dir,
163            'where': strWhere,
164            'parentWhere': parentWhere,
165            'tmpWhere': tmpWhere,
166            }
167   DBG.write("git_extract_sub_dir", aDict)
168   if not src.architecture.is_windows():
169     cmd = r"""
170 set -x
171 export tmpDir=%(tmpWhere)s && \
172 rm -rf $tmpDir
173 git clone %(remote)s $tmpDir && \
174 cd $tmpDir && \
175 git checkout %(tag)s && \
176 mv %(sub_dir)s %(where)s && \
177 git log -1 > %(where)s/README_git_log.txt && \
178 rm -rf $tmpDir
179 """ % aDict
180   else:
181     cmd = r"""
182
183 set tmpDir=%(tmpWhere)s && \
184 rm -rf $tmpDir
185 git clone %(remote)s $tmpDir && \
186 cd $tmpDir && \
187 git checkout %(tag)s && \
188 mv %(sub_dir)s %(where)s && \
189 git log -1 > %(where)s/README_git_log.txt && \
190 rm -rf $tmpDir
191 """ % aDict
192
193   DBG.write("cmd", cmd)
194
195   for nbtry in range(0,3): # retries case of network problem
196     rc = UTS.Popen(cmd, cwd=parentWhere, env=environment.environ.environ, logger=logger)
197     if rc.isOk(): break
198     time.sleep(30) # wait a little
199
200   return rc.isOk()
201
202 def archive_extract(from_what, where, logger):
203     '''Extracts sources from an archive.
204     
205     :param from_what str: The path to the archive.
206     :param where str: The path where to extract.
207     :param logger Logger: The logger instance to use.
208     :return: True if the extraction is successful
209     :rtype: boolean
210     '''
211     try:
212         archive = tarfile.open(from_what)
213         for i in archive.getmembers():
214             archive.extract(i, path=str(where))
215         return True, os.path.commonprefix(archive.getnames())
216     except Exception as exc:
217         logger.write("archive_extract: %s\n" % exc)
218         return False, None
219
220 def cvs_extract(protocol, user, server, base, tag, product, where,
221                 logger, checkout=False, environment=None):
222     '''Extracts sources from a cvs repository.
223     
224     :param protocol str: The cvs protocol.
225     :param user str: The user to be used.
226     :param server str: The remote cvs server.
227     :param base str: .
228     :param tag str: The tag.
229     :param product str: The product.
230     :param where str: The path where to extract.
231     :param logger Logger: The logger instance to use.
232     :param checkout boolean: If true use checkout cvs.
233     :param environment src.environment.Environ: The environment to source when
234                                                 extracting.
235     :return: True if the extraction is successful
236     :rtype: boolean
237     '''
238
239     opttag = ''
240     if tag is not None and len(tag) > 0:
241         opttag = '-r ' + tag
242
243     cmd = 'export'
244     if checkout:
245         cmd = 'checkout'
246     elif len(opttag) == 0:
247         opttag = '-DNOW'
248     
249     if len(protocol) > 0:
250         root = "%s@%s:%s" % (user, server, base)
251         command = "cvs -d :%(protocol)s:%(root)s %(command)s -d %(where)s %(tag)s %(product)s" % \
252             { 'protocol': protocol, 'root': root, 'where': str(where.base()),
253               'tag': opttag, 'product': product, 'command': cmd }
254     else:
255         command = "cvs -d %(root)s %(command)s -d %(where)s %(tag)s %(base)s/%(product)s" % \
256             { 'root': server, 'base': base, 'where': str(where.base()),
257               'tag': opttag, 'product': product, 'command': cmd }
258
259     logger.write(command + "\n", 5)
260
261     if not where.dir().exists():
262         where.dir().make()
263
264     logger.logTxtFile.write("\n" + command + "\n")
265     logger.logTxtFile.flush()        
266     res = subprocess.call(command,
267                           cwd=str(where.dir()),
268                           env=environment.environ.environ,
269                           shell=True,
270                           stdout=logger.logTxtFile,
271                           stderr=subprocess.STDOUT)
272     return (res == 0)
273
274 def svn_extract(user,
275                 from_what,
276                 tag,
277                 where,
278                 logger,
279                 checkout=False,
280                 environment=None):
281     '''Extracts sources from a svn repository.
282     
283     :param user str: The user to be used.
284     :param from_what str: The remote git repository.
285     :param tag str: The tag.
286     :param where str: The path where to extract.
287     :param logger Logger: The logger instance to use.
288     :param checkout boolean: If true use checkout svn.
289     :param environment src.environment.Environ: The environment to source when
290                                                 extracting.
291     :return: True if the extraction is successful
292     :rtype: boolean
293     '''
294     if not where.exists():
295         where.make()
296
297     if checkout:
298         command = "svn checkout --username %(user)s %(remote)s %(where)s" % \
299             { 'remote': from_what, 'user' : user, 'where': str(where) }
300     else:
301         command = ""
302         if os.path.exists(str(where)):
303             command = "/bin/rm -rf %(where)s && " % \
304                 { 'remote': from_what, 'where': str(where) }
305         
306         if tag == "master":
307             command += "svn export --username %(user)s %(remote)s %(where)s" % \
308                 { 'remote': from_what, 'user' : user, 'where': str(where) }       
309         else:
310             command += "svn export -r %(tag)s --username %(user)s %(remote)s %(where)s" % \
311                 { 'tag' : tag, 'remote': from_what, 'user' : user, 'where': str(where) }
312     
313     logger.logTxtFile.write(command + "\n")
314     
315     logger.write(command + "\n", 5)
316     logger.logTxtFile.write("\n" + command + "\n")
317     logger.logTxtFile.flush()
318     res = subprocess.call(command,
319                           cwd=str(where.dir()),
320                           env=environment.environ.environ,
321                           shell=True,
322                           stdout=logger.logTxtFile,
323                           stderr=subprocess.STDOUT)
324     return (res == 0)
325
326 def get_pkg_check_cmd():
327     '''Build the command to use for checking if a linux package is installed or not.'''
328     # 1- search for an installed package manager (rpm on rh, apt on db)
329     cmd_which_rpm=["which", "rpm"]
330     cmd_which_apt=["which", "apt"]
331     with open(os.devnull, 'w') as devnull:
332         # 1) we search for apt (debian based systems)
333         completed=subprocess.call(cmd_which_apt,stdout=devnull, stderr=subprocess.STDOUT)
334         if completed==0:
335             cmd_is_package_installed=["apt", "list", "--installed"]
336         else:
337             # 2) if apt not found search for rpm (redhat)
338             completed=subprocess.call(cmd_which_rpm,stdout=devnull, stderr=subprocess.STDOUT) # only 3.8! ,capture_output=True)
339             if completed==0:
340                 cmd_is_package_installed=["rpm", "-q"]
341             else:
342                 # no package manager was found
343                 raise src.SatException("Error : command failed because sat was not able to find apt or rpm")
344     return cmd_is_package_installed
345
346 def check_system_pkg(check_cmd,pkg):
347     '''Check if a package is installed
348     :param check_cmd list: the list of command to use system package manager
349     :param user str: the pkg name to check
350     :rtype: str
351     :return: a string with package name with status un message
352     '''
353     # build command
354     cmd_is_package_installed=[]
355     for cmd in check_cmd:
356         cmd_is_package_installed.append(cmd)
357     cmd_is_package_installed.append(pkg)
358     if check_cmd[0]=="apt":
359         # special treatment for apt
360         # (some debian packages have version numbers in their name, and also
361         # apt do not return status)
362         cmd_is_package_installed[-1]+="*" # we don't specify in pyconf the exact name because of version numbers
363         cmd_is_package_installed.append('|')
364         cmd_is_package_installed.append('grep') # add a grep to get an exit status
365         cmd_is_package_installed.append(cmd) 
366         
367     p=subprocess.Popen(cmd_is_package_installed, 
368                        stdout=subprocess.PIPE, 
369                        stderr=subprocess.PIPE)
370     output, err = p.communicate()
371     rc = p.returncode
372     if rc==0:
373         check_res=src.printcolors.printcSuccess("OK")
374         if check_cmd[0]=="rpm": # apt output is too messy for being used
375             check_res+=" (" + output.replace('\n',' ') + ")\n" # remove output trailing \n
376     else:
377         check_res=src.printcolors.printcError("KO") 
378         check_res+=" (package is not installed!)\n"
379
380     msg_status="\n   - "+pkg + " : " + check_res
381     return msg_status
382