Home | History | Annotate | Download | only in base_definitions
      1 #
      2 # Copyright (C) 2016 The Android Open Source Project
      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 
     17 import itertools
     18 
     19 from vts.runners.host import const
     20 
     21 
     22 class CheckSetupCleanup(object):
     23     """An abstract class for defining environment setup job.
     24 
     25     Usually such a job contains check -> setup -> cleanup workflow
     26 
     27     Attributes:
     28         to_check: bool, whether or not to check the defined environment
     29                   requirement. Default: True
     30         to_setup: bool, whether or not to setup the defined environment
     31                   requirement. Default: False
     32         to_cleanup: bool, whether or not to cleanup the defined environment
     33                     requirement if it is set up by this class. Default: False
     34         context: ShellEnvironment object that provides AddCleanupJobs and
     35                  ExecuteShellCommand method
     36         note: string, used by GetNote to generate error message if fail.
     37     """
     38 
     39     def __init__(self):
     40         self.to_check = True
     41         self.to_setup = False
     42         self.to_cleanup = False
     43         self.context = None
     44         self.note = None
     45 
     46     @property
     47     def note(self):
     48         return self._note
     49 
     50     @note.setter
     51     def note(self, note):
     52         self._note = note
     53 
     54     @property
     55     def context(self):
     56         return self._context
     57 
     58     @context.setter
     59     def context(self, context):
     60         self._context = context
     61 
     62     def Execute(self):
     63         """Execute the check, setup, and cleanup.
     64 
     65         Will execute setup and cleanup only if the boolean switches for them
     66         are True. It will NOT execute cleanup if check function passes.
     67 
     68            Return:
     69                tuple(bool, string), a tuple of True and empty string if success,
     70                                     a tuple of False and error message if fail.
     71         """
     72 
     73         if self.context is None:
     74             self.note = "Error: Environment definition context not set"
     75             return False
     76 
     77         if not self.InternalCall(self.ValidateInputs):
     78             return False
     79 
     80         check_result = False
     81         if self.to_check:
     82             check_result = self.InternalCall(self.Check)
     83 
     84         if check_result or not self.to_setup:
     85             return check_result
     86 
     87         if self.to_cleanup:
     88             self.context.AddCleanupJob(self.InternalCall, self.Cleanup)
     89 
     90         return self.InternalCall(self.Setup)
     91 
     92     def __str__(self):
     93         return ("Shell Environment Check Definition Class:{cls} "
     94                 "Variables: {props}").format(
     95                     cls=self.__class__.__name__, props=vars(self))
     96 
     97     def GetNote(self):
     98         """Get a string note as error message. Can be override by sub-class"""
     99         if not self.note:
    100             self.note = "Shell environment definition unsatisfied"
    101         return "{}\nat: {}".format(self.note, self)
    102 
    103     def InternalCall(self, method):
    104         """Internal method to call sub class inherited methods.
    105 
    106         It call the function, check results, and put failure note if not set
    107         """
    108         self.note = None
    109         success = method()
    110         if not success and not self.note:
    111             self.note = ("Shell environment definition unsatisfied: step [%s] "
    112                          "failed.") % method.__name__
    113         return success
    114 
    115     def ValidateInputs(self):
    116         """Validate input parameters. Can be override by sub-class
    117 
    118         Return:
    119             tuple(bool, string), a tuple of True and empty string if pass,
    120                                  a tuple of False and error message if fail.
    121         """
    122         return True
    123 
    124     def ToListLike(self, obj):
    125         """Convert single item to list of single item.
    126 
    127         If input is already a list like object, the same object will
    128         be returned.
    129         This method is for the convenience of writing child class.
    130 
    131         Arguments:
    132             obj: any object
    133         """
    134         if not self.IsListLike(obj):
    135             return [obj]
    136         else:
    137             return obj
    138 
    139     def IsListLike(self, obj):
    140         """Checks whether a object is list-like.
    141 
    142         This method is for the convenience of writing child class.
    143         It will check for existence of __iter__ and __getitem__
    144         in attributes of the object.
    145         String is not considered list-like, tuple is.
    146 
    147         Arguments:
    148             obj: any object
    149         """
    150         return hasattr(obj, '__iter__') and hasattr(obj, '__getitem__')
    151 
    152     def NormalizeInputLists(self, *inputs):
    153         """Normalize inputs to lists of same length.
    154 
    155         This method is for the convenience of writing child class.
    156         If there are lists in inputs, they should all be of same length;
    157         otherwise, None is returned.
    158         If there are lists and single items in inputs, single items are
    159         duplicated to a list of other list's length.
    160         If there are only single items in inputs, they all get converted to
    161         single item lists.
    162 
    163         Arguments:
    164             inputs: any inputs
    165 
    166         Return:
    167             a generator of normalized inputs
    168         """
    169         # If any of inputs is None or empty, inputs are considered not valid
    170         # Definition classes wish to take None input should not use this method
    171         if not all(inputs):
    172             return None
    173 
    174         lists = filter(self.IsListLike, inputs)
    175         if not lists:
    176             # All inputs are single items. Convert them to lists
    177             return ([i] for i in inputs)
    178 
    179         lengths = set(map(len, lists))
    180         if len(lengths) != 1:
    181             # lists in inputs have different lengths, cannot normalize
    182             return None
    183         length = lengths.pop()
    184 
    185         return (i if self.IsListLike(i) else list(itertools.repeat(i, length))
    186                 for i in inputs)
    187 
    188     def Check(self):
    189         """Check function for the class.
    190 
    191         Used to check environment. Can be override by sub-class
    192         """
    193         self.note = "Check step undefined."
    194         return False
    195 
    196     def Setup(self):
    197         """Check function for the class.
    198 
    199         Used to setup environment if check fail. Can be override by sub-class
    200         """
    201         self.note = "Setup step undefined."
    202         return False
    203 
    204     def Cleanup(self):
    205         """Check function for the class.
    206 
    207         Used to cleanup setup if check fail. Can be override by sub-class
    208         """
    209         self.note = "Cleanup step undefined."
    210         return False
    211 
    212     @property
    213     def shell(self):
    214         """returns an object that can execute a shell command"""
    215         return self.context.shell
    216 
    217     def ExecuteShellCommand(self, cmd):
    218         """Execute a shell command or a list of shell commands.
    219 
    220         Args:
    221             command: str, the command to execute; Or
    222                      list of str, a list of commands to execute
    223 
    224         return:
    225             A dictionary containing results in format:
    226                 {const.STDOUT: [stdouts],
    227                  const.STDERR: [stderrs],
    228                  const.EXIT_CODE: [exit_codes]}.
    229         """
    230         return self.shell.Execute(cmd)
    231