def isScheduleValid(self, schedule, node_srvs, force) -> (bool, str):
        """
        Validates schedule of planned node upgrades

        :param schedule: dictionary of node ids and upgrade times
        :param node_srvs: dictionary of node ids and services
        :return: a 2-tuple of whether schedule valid or not and the reason
        """

        # flag "force=True" ignore basic checks! only datetime format is
        # checked
        times = []
        non_demoted_nodes = set([k for k, v in node_srvs.items() if v])
        if not force and set(schedule.keys()) != non_demoted_nodes:
            return False, 'Schedule should contain id of all nodes'
        now = datetime.utcnow().replace(tzinfo=dateutil.tz.tzutc())
        for dateStr in schedule.values():
            try:
                when = dateutil.parser.parse(dateStr)
                if when <= now and not force:
                    return False, '{} is less than current time'.format(when)
                times.append(when)
            except ValueError:
                return False, '{} cannot be parsed to a time'.format(dateStr)
        if force:
            return True, ''
        times = sorted(times)
        for i in range(len(times) - 1):
            diff = (times[i + 1] - times[i]).total_seconds()
            if diff < self.config.MinSepBetweenNodeUpgrades:
                return False, 'time span between upgrades is {} ' \
                              'seconds which is less than specified ' \
                              'in the config'.format(diff)
        return True, ''
def isScheduleValid ( self , schedule , node_srvs , force ) -> ( bool , str ) : times = [ ] non_demoted_nodes = set ( [ k for k , v in node_srvs . items ( ) if v ] ) if not force and set ( schedule . keys ( ) ) != non_demoted_nodes : return False , 'Schedule should contain id of all nodes' now = datetime . utcnow ( ) . replace ( tzinfo = dateutil . tz . tzutc ( ) ) for dateStr in schedule . values ( ) : try : when = dateutil . parser . parse ( dateStr ) if when <= now and not force : return False , '{} is less than current time' . format ( when ) times . append ( when ) except ValueError : return False , '{} cannot be parsed to a time' . format ( dateStr ) if force : return True , '' times = sorted ( times ) for i in range ( len ( times ) - 1 ) : diff = ( times [ i + 1 ] - times [ i ] ) . total_seconds ( ) if diff < self . config . MinSepBetweenNodeUpgrades : return False , 'time span between upgrades is {} ' 'seconds which is less than specified ' 'in the config' . format ( diff ) return True , ''
Validates schedule of planned node upgrades

        :param schedule: dictionary of node ids and upgrade times
        :param node_srvs: dictionary of node ids and services
        :return: a 2-tuple of whether schedule valid or not and the reason