1#!/usr/bin/python 2# Copyright 2016 Google Inc. 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 16"""Execute user provided metadata scripts.""" 17 18import os 19import stat 20import subprocess 21 22 23class ScriptExecutor(object): 24 """A class for executing user provided metadata scripts.""" 25 26 def __init__(self, logger, script_type, default_shell=None): 27 """Constructor. 28 29 Args: 30 logger: logger object, used to write to SysLog and serial port. 31 script_type: string, the type of the script we are running. 32 default_shell: string, the default shell to execute the script. 33 """ 34 self.logger = logger 35 self.script_type = script_type 36 self.default_shell = default_shell or '/bin/bash' 37 38 def _MakeExecutable(self, metadata_script): 39 """Add executable permissions to a file. 40 41 Args: 42 metadata_script: string, the path to the executable file. 43 """ 44 mode = os.stat(metadata_script).st_mode 45 os.chmod(metadata_script, mode | stat.S_IEXEC) 46 47 def _RunScript(self, metadata_key, metadata_script): 48 """Run a script and log the streamed script output. 49 50 Args: 51 metadata_key: string, the key specifing the metadata script. 52 metadata_script: string, the file location of an executable script. 53 """ 54 process = subprocess.Popen( 55 metadata_script, shell=True, 56 executable=self.default_shell, 57 stderr=subprocess.STDOUT, stdout=subprocess.PIPE) 58 while True: 59 for line in iter(process.stdout.readline, b''): 60 message = line.decode('utf-8', 'replace').rstrip('\n') 61 if message: 62 self.logger.info('%s: %s', metadata_key, message) 63 if process.poll() is not None: 64 break 65 self.logger.info('%s: Return code %s.', metadata_key, process.returncode) 66 67 def RunScripts(self, script_dict): 68 """Run the metadata scripts; execute a URL script first if one is provided. 69 70 Args: 71 script_dict: a dictionary mapping metadata keys to script files. 72 """ 73 metadata_types = ['%s-script-url', '%s-script'] 74 metadata_keys = [key % self.script_type for key in metadata_types] 75 metadata_keys = [key for key in metadata_keys if script_dict.get(key)] 76 if not metadata_keys: 77 self.logger.info('No %s scripts found in metadata.', self.script_type) 78 for metadata_key in metadata_keys: 79 metadata_script = script_dict.get(metadata_key) 80 self._MakeExecutable(metadata_script) 81 self._RunScript(metadata_key, metadata_script) 82