def testCompositeSpec(self): a, b = 5, 7 ab = a * b tObj = TestObj2(a, b) spec1 = Specification('t.b == b', candidate_name='t') spec2 = Specification('t.ab < ab', candidate_name='t') spec3 = Specification('1 <= t.a <= 7') cspec = spec1 & spec2 & spec3 self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = spec1 | spec2 | spec3 self.assertTrue(cspec.is_satisfied_by(tObj)) cspec = spec1 ^ spec2 ^ spec3 self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = spec1 & (spec2 & spec3) self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = (spec1 & spec2) | spec3 self.assertTrue(cspec.is_satisfied_by(tObj)) cspec = (spec1 | spec2) & ~spec3 self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = (~spec1 | spec2) | ~spec3 self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = (spec1 & ~spec2) & spec3 self.assertTrue(cspec.is_satisfied_by(tObj)) cspec = (spec1 & ~spec2) ^ spec3 self.assertFalse(cspec.is_satisfied_by(tObj)) cspec = (spec1 ^ spec2) & spec3 self.assertTrue(cspec.is_satisfied_by(tObj)) cspec = ~(spec1 ^ spec2) ^ spec3 self.assertTrue(cspec.is_satisfied_by(tObj)) cspec = ~(spec1 ^ spec2 ^ spec3) self.assertTrue(cspec.is_satisfied_by(tObj, ab=ab))
def test_eq_and_hash(self): def op(x, y): return x == y # pragma: no cover spec1 = Specification("op(a, 1)", candidate_name='a') spec2 = Specification("op(a,1)", candidate_name='a') spec3 = Specification("a == 1") self.assertEqual(spec1, spec2) self.assertEqual(hash(spec1), hash(spec2)) self.assertNotEqual(spec1, spec3) self.assertNotEqual(hash(spec1), hash(spec3)) negspec1 = ~spec1 negspec2 = ~spec2 negspec3 = ~spec3 self.assertEqual(negspec1, negspec2) self.assertEqual(hash(negspec1), hash(negspec2)) self.assertNotEqual(negspec1, negspec3) self.assertNotEqual(hash(negspec1), hash(negspec3)) self.assertNotEqual(spec1, negspec1) self.assertNotEqual(hash(spec1), hash(negspec1)) cspec1 = spec1 & negspec2 cspec2 = spec2 & negspec1 cspec3 = spec1 & spec3 self.assertEqual(cspec1, cspec2) self.assertEqual(hash(cspec1), hash(cspec2)) self.assertNotEqual(cspec1, cspec3) self.assertNotEqual(hash(cspec1), hash(cspec3)) self.assertNotEqual(spec1, cspec1) self.assertNotEqual(hash(spec1), hash(cspec1))
def __load_specifications(self): """ Loads Specifications from the configurations and stores them in the internal storage. """ try: params = rospy.get_param(self.__namespace) if isinstance(params, dict): specifications = [] for x in params.values(): for y in x: specifications.append(y) else: specifications = params for o in specifications: for seuid in o.keys(): if SEUID().is_valid(seuid): spec = Specification() spec.seuid = seuid for k in o[seuid].keys(): spec.add_tuple((k, o[seuid][k])) self.__specifications[seuid] = spec else: rospy.logdebug("[SpecificationHandler][__load_specifications] %s is not a valid seuid." % seuid) except KeyError: pass rospy.loginfo("[SpecificationHandler] Loaded %s parameters." % str(len(self.__specifications.keys())))
def testCompositeSpec(self): spec1 = Specification('b == 2') spec2 = Specification('ab > 20') spec3 = Specification('2 < a <= 7') spec4 = Specification('ab not in range(-12, -7)') for op in (operator.and_, operator.or_, operator.xor): for spec1, spec2 in combinations((spec1, spec2, spec3, spec4), 2): spec = op(spec1, spec2) neg_spec = ~spec self.assertTrue(isinstance(neg_spec, Specification)) self.assertEqual(spec, ~neg_spec)
def __init__(self, centralised_vcs_server, target_test_coverage_per_feature=1.0, tests_per_chunk_ratio=1, target_dependencies_per_feature=0 ): change_management = ChangeManagement(centralised_vcs_server) self.specification = Specification(change_management) self.testing = Testing(change_management, target_test_coverage_per_feature, tests_per_chunk_ratio) self.implementation = Implementation(change_management) self.debugging = Debugging(change_management) self.refactoring = Refactoring(change_management, target_dependencies_per_feature)
def testSpecFromExpression(self): src = "x==1" expr = ast.parse(src, mode='eval') spec = Specification(expr) self.assertIsInstance(spec._ast_expr, ast.Expression) self.assertEqual(spec._candidate_name, 'x') self.assertEqual(spec._context, {})
def testAdditionalContext(self): year = 1999 spec = Specification('deadline < date(year, 6, 30)', candidate_name='deadline') dt = date(year, 5, 1) self.assertTrue(spec(dt)) self.assertTrue(spec(dt, year=2000)) self.assertFalse(spec(dt, year=1997))
def watch_spec(data, stat): if data: self.logger.debug('Reloading spec.yaml') self.spec = Specification(data) self.logger.debug('Parsed specification') else: self.spec = None self.logger.warn('Specification not found in {}'.format(BlacKnightClient.spec_znode))
class TestIssue1(unittest.TestCase): def setUp(self): from decimal import Decimal # noqa self.spec = Specification("x == Decimal('5.4')", candidate_name='x') def testIsSatisfied(self): self.assertFalse(self.spec.is_satisfied_by(5))
def testNestedproperty(self): a, b = 5, 7 ab = a * b tObj = TestObj1(a=3, b=TestObj2(a, b)) spec = Specification(f't.b.ab == {ab}') self.assertTrue(spec.is_satisfied_by(tObj)) spec = ~Specification(f't.b.ab != {ab}') self.assertTrue(spec.is_satisfied_by(tObj)) ispec = Specification('1 <= t.b.a <= 7') self.assertTrue(ispec.is_satisfied_by(tObj)) cspec = spec & ispec self.assertTrue(cspec.is_satisfied_by(tObj))
def testSpecFromStr(self): expr = "b == 2" spec = Specification(expr) self.assertIsInstance(spec._ast_expr, ast.Expression) self.assertEqual(spec._candidate_name, 'b') self.assertEqual(spec._context, {}) # nested attribute expr = "b.x.y==2" spec = Specification(expr) self.assertIsInstance(spec._ast_expr, ast.Expression) self.assertEqual(spec._candidate_name, 'b') self.assertEqual(spec._context, {}) # nested attribute and context variables y = object() z = object() expr = "y.a > x.b >= z.c" spec = Specification(expr, candidate_name='x') self.assertIsInstance(spec._ast_expr, ast.Expression) self.assertEqual(spec._candidate_name, 'x') self.assertEqual(spec._context, {'y': y, 'z': z})
def testReprAndStr(self): op = lambda x, y: x == y # noqa spec = Specification('op(o.a,1)', candidate_name='o') self.assertEqual(repr(spec), "Specification('op(o.a, 1)', candidate_name='o')") self.assertEqual(str(spec), '<o: op(o.a, 1)>') self.assertEqual(repr(~spec), "Specification('not op(o.a, 1)', " "candidate_name='o')") self.assertEqual(str(~spec), '<o: not op(o.a, 1)>') spec = Specification('x.a == 1') self.assertEqual(repr(spec), "Specification('x.a == 1')") self.assertEqual(str(spec), '<x: x.a == 1>') ispec = Specification('0 < a.b <=6') self.assertEqual(repr(ispec), "Specification('0 < a.b <= 6')") self.assertEqual(str(ispec), '<a: 0 < a.b <= 6>') cspec = ispec & spec self.assertEqual(repr(cspec), "Specification('0 < a.b <= 6 and a.a == 1')") self.assertEqual(str(cspec), '<a: 0 < a.b <= 6 and a.a == 1>')
def testSimpleSpec(self): for op1, op2 in [('==', '!='), ('<', '>='), ('>', '<='), ('is', 'is not')]: spec1 = Specification(f'a {op1} 1') spec2 = Specification(f'a {op2} 1') neg_spec1 = ~spec1 neg_spec2 = ~spec2 self.assertTrue(isinstance(neg_spec1, Specification)) self.assertTrue(isinstance(neg_spec2, Specification)) self.assertEqual(spec1, neg_spec2) self.assertEqual(spec2, neg_spec1) self.assertEqual(spec1, ~neg_spec1) self.assertEqual(spec2, ~neg_spec2) # non-predefined operator: op = lambda x, y: x == y # noqa spec = Specification(f'op(a, 1)', candidate_name='a') neg_spec = ~spec self.assertTrue(isinstance(neg_spec, Specification)) self.assertEqual(spec, ~neg_spec)
def test_find(self): repo = self.repo(Person) p1 = Person('Hans', 'Berlinger', date(1982, 12, 3)) self.assertIsNone(repo.add(p1)) p2 = Person('Bert', 'Berlinger', date(1962, 2, 14)) self.assertIsNone(repo.add(p2)) p3 = Prospect('Hans', 'Berliner', date(1982, 12, 3)) self.assertIsNone(repo.add(p3)) spec = Specification('x.date_of_birth >= date(1980, 1, 1)', candidate_name='x') res = list(repo.find(spec)) self.assertEqual(len(res), 2) self.assertIn(p1, res) self.assertIn(p3, res)
class TestDrivenDevelopment(object): is_workflow = True def __init__(self, centralised_vcs_server, target_test_coverage_per_feature=1.0, tests_per_chunk_ratio=1, target_dependencies_per_feature=0 ): change_management = ChangeManagement(centralised_vcs_server) self.specification = Specification(change_management) self.testing = Testing(change_management, target_test_coverage_per_feature, tests_per_chunk_ratio) self.implementation = Implementation(change_management) self.debugging = Debugging(change_management) self.refactoring = Refactoring(change_management, target_dependencies_per_feature) @default_cost() def implement_feature_tdd(self, user_story, random): """ Implements the sequence of activities in a tests driven development workflow. """ self.specification.add_feature(user_story.logical_name, user_story.size, random) self.testing.test_per_chunk_ratio(user_story.logical_name, random) self.implementation.implement_feature(user_story.logical_name, random) self.debugging.debug_feature(user_story.logical_name, random) self.refactoring.refactor_feature(user_story.logical_name, random) @default_cost() def work_from_backlog(self, product_backlog, random): while True: try: user_story = product_backlog.get(block=False) self.implement_feature_tdd(user_story, random) except Empty: break
def setUp(self): self.x = x = object() # noqa self.y = y = 5 # noqa self.z = z = 13 # noqa self.spec1 = Specification("y == 2") self.spec2 = Specification("ab>20") self.spec3 = Specification("x==y", candidate_name='x') self.spec4 = Specification("x.ab>=z", candidate_name='x') self.spec5 = Specification("x.ab>=z", candidate_name='z') self.spec6 = Specification("x.ab>=z", candidate_name='x', namespace = {'z': 7})
def testSimpleSpec(self): val = 5 tObj = TestObj1(b=val) for op1, op2 in [(operator.eq, operator.ne), (operator.lt, operator.ge), (operator.gt, operator.le), (operator.is_, operator.is_not)]: spec1 = Specification(f't.b {op2sym[op1]} {val}') spec2 = Specification(f't.b {op2sym[op2]} {val}') self.assertEqual(spec1.is_satisfied_by(tObj), op1(tObj.b, val)) self.assertFalse(spec1.is_satisfied_by(tObj) and spec2.is_satisfied_by(tObj)) spec1 = Specification(f't.b {op2sym[op1]} {-val}') spec2 = Specification(f't.b {op2sym[op2]} {-val}') self.assertEqual(spec1.is_satisfied_by(tObj), op1(tObj.b, -val)) self.assertFalse(spec1.is_satisfied_by(tObj) and spec2.is_satisfied_by(tObj)) # compare instance of subclass tObj = TestObj2(b=val) spec = Specification(f't.b == {val}') self.assertTrue(spec.is_satisfied_by(tObj)) spec = Specification(f't.b < {val}') self.assertFalse(spec.is_satisfied_by(tObj))
def testNameConflict(self): spec = Specification('dt >= date(2020, 1, 1)', candidate_name='dt') # conflict with given keyword candidate = date(2021, 8, 1) self.assertRaises(ValueError, spec, candidate, date=date, dt=date.today())
def run(self): from nusmv import dd import marduk_utils import time start_wall_clock = time.clock() self.println("------------------------------------------------------") self.println("Synthesize " + str(self.input_file) + " to " + str(self.output_file)) if self.__options.mode != None: self.println("Mode: " + self.__options.mode.upper()) if self.__options.language != None: self.println("Output Language: " + self.__options.language.upper()) from datetime import datetime self.println("Start time: %s" % datetime.now().ctime()) #print "Reordering status:", dd.dd_reordering_status(dd.cvar.dd_manager) self.println("\n Dynamic reordering \t\t\t\t\t" + str(self.dyn_reorder)) self.println(" Reordering method for dynamic reordering \t\t" + marduk_utils.reorder_method_to_string(self.dyn_reorder_method)) self.println(" Reordering method for forced reordering \t\t" + marduk_utils.reorder_method_to_string(self.reorder_method)) self.println(" Reorder BDD after reading configuration \t\t" + str(self.r1)) self.println(" Reorder BDD after generating output functions \t" + str(self.r2)) self.println(" Kill strategy and reorder afterwards \t\t" + str(self.kill)) self.println(" One-hot encoding of jx vars\t\t\t\t" + str(self.one_hot)) self.println(" Transfer functions to new DD manager and reorder \t" + str(self.transfer_functions)) self.println(" Generate functions according to\n Baneres, Cortadella, Kishinevsky (DAC'04)\t\t" + str(self.dac04)) if self.dac04: if self.dac_search_mode == 'dfs': self.println(" DAC'04 search mode \t\t\t\t\tDepth First") self.println(" DAC'04 recursion depth limit\t\t\t\t" + str(self.dac_recur_limit)) elif self.dac_search_mode == 'bfs': self.println(" DAC'04 search mode \t\t\t\t\tBreadth First") self.println(" DAC'04 call limit\t\t\t\t\t" + str(self.dac_call_limit)) if self.__mode in (marduk_utils.Modes.IRRSOP, marduk_utils.Modes.FACTOR): if self.cache_size != None: self.println(" Function cache size \t\t\t\t\t" + str(self.cache_size)) else: self.println(" Function cache size \t\t\t\t\tunlimited") self.println(" Number of class vectors \t\t\t\t" + str(self.num_class_vectors)) self.println(" Number of new vectors for cache reorganization \t" + str(self.update_sigs_size)) self.println(" Use don't care upper bound for ISoP_d \t\t\t" + str(self.dont_care_upper_bound)) self.println(" Check combinations of 2 functions \t\t\t" + str(self.check_combinations)) self._starttime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println("\n Timing Information:") # Load specification from input file self.__specification = Specification(self) self.__specification.readSpecification() self._specificationtime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Specification read within\t\t\t %7.2f seconds" %(self._specificationtime-self._starttime)) # Create list of variables and store it here in main class, for central access self.__variables = self.__specification.create_variable_list() if self.verbose > 1: print "Length of variables list: ", len(self.__variables) for var in self.__variables: print var if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) if self.var_order: self.println(' Forcing variable order: ' + self.var_order) marduk_utils.set_variable_ordering(self.var_order, self.vars, self.dd_mgr) if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) # Compute winning region import sys self.__winning_region = WinningRegion(self, self.__specification) self.__winning_region.calcWinningRegion() self._winningregiontime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Compute winning region within \t\t %7.2f seconds" %(self._winningregiontime - self._reorder1time)) if(not(self.__winning_region.isRealizable())): self.println("The given specification is NOT REALIZABLE!\n") if not self.debug_mode: self.println("Use the argument --dm to debug unrealizability\n") return del self.__winning_region self.__spec_debugger = SpecDebugger(self) self.__spec_debugger.debug(self.debug_mode) return self.__winning_region_size = self.__winning_region.winRegion.size # Compute strategy self.__strategy = Strategy(self) self.__strategy.calcStrategy() self._strategytime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Compute strategy within \t\t\t %7.2f seconds" %(self._strategytime-self._winningregiontime)) from marduk_utils import VariableType self.println("\nStrategy Characterization:") input_vars = [var.ns for var in self.input_vars] + [var.ps for var in self.vars] output_vars = [var.ns for var in self.vars if var.type != VariableType.INPUT] begin = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime strat_char = marduk_utils.characterize_relation(self.__strategy.strategy_bdd, input_vars, output_vars, return_bdds=True) char_time = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime - begin self.println("(computed in %7.2f seconds)" % char_time) total = 2 ** strat_char['num_inputs'] defined = (strat_char['num_defined'] / total) * 100 fixed = (strat_char['num_fixed'] / total) * 100 dc = (strat_char['num_dc'] / total) * 100 non_dc = (strat_char['num_non_dc'] / total) * 100 self.println("Defined:%7.2f%%" % defined) self.println("Fixed:\t %7.2f%%" % fixed) self.println("DC:\t %7.2f%%" % dc) self.println("Non-DC:\t %7.2f%%\n" % non_dc) strat_dc = strat_char['dc_bdd'] del strat_char if self.__mode in (marduk_utils.Modes.COFACTOR, marduk_utils.Modes.OLD): self.do_cofactor_mode() else: self.do_function_generator_mode(strat_dc=strat_dc) if self.transfer_functions: self.println(" Transferred output functions within \t\t %7.2f seconds" % self.__code_generator._transfer_time) self.println(" Reordering transferred BDD took \t\t %7.2f seconds" % self.__code_generator._reorder_time) self.println("\n Results in needed overall time of\t\t %7.2f seconds \n" %(self._codegentime - self._starttime)) stop_wall_clock_time = time.clock() self.println(" Overall wall clock time\t\t\t %7.2f seconds" % (stop_wall_clock_time - start_wall_clock)) self.println("\n BDD-Size Information:") self.println(" Size of Winning Region: \t\t %10d bdd-nodes" % self.__winning_region_size) self.println(" Size of rho1 is \t\t\t %10d bdd-nodes" %self.__strategy.rho1_size) self.println(" Size of rho2 is \t\t\t %10d bdd-nodes" %self.__strategy.rho2_size) self.println(" Size of rho3 is \t\t\t %10d bdd-nodes" %self.__strategy.rho3_size) self.println("------------------------------------------------------") self.println(" FINISHED synthesis!" ) self.println("------------------------------------------------------") if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) # The following should be the last block in the run method: self.__code_generator.append_comment(self.__printed_lines) # Killing references to other objects, break up circular references self.__specification = None self.__winning_region = None self.__strategy = None self.__output_functions = None self.__code_generator = None
class Marduk(object): """ This is the main class of the Marduk implementation. It is the main start of control flow. Furthermore it stores global options, parameters, and other objects and provides appropriate access methods. """ def __init__(self, options, args): """ Marduk constructor. Analyzes and stores options. """ self.__options = options self.__args = args # Initialize DD manager for synthesis. from nusmv import dd self.__dd_manager = dd.create_dd_manager(0,0,251,131071,0) # Values from PerlDD self.__printed_lines = ["This file was automatically synthesized with Marduk.", "Information on the synthesis process is displayed below.", "---------------------------------------------------------------------------\n"] if options.input_file == None: self.println("WARNING: No input file specified! Using 'input.xml' as default input file!") self.__input_file = "input.xml" else: self.__input_file = options.input_file if options.output_file == None: self.println("WARNING: No output file specified! Using 'marduk_out' as default output file!") self.__output_file = "marduk_out" else: self.__output_file = options.output_file if options.mode == None: self.println("WARNING: No mode specified! Using 'cofactor' as default!") self.__mode = marduk_utils.Modes.COFACTOR elif options.mode.lower() == "cofactor": self.__mode = marduk_utils.Modes.COFACTOR elif options.mode.lower() == "irrsop": self.__mode = marduk_utils.Modes.IRRSOP elif options.mode.lower() == "factor": self.__mode = marduk_utils.Modes.FACTOR elif options.mode.lower() == "old": self.__mode = marduk_utils.Modes.OLD self.println("WARNING: In 'OLD' mode, the language setting will be ignored. Output will be in BLIF format.") else: self.println("WARNING: Unknown mode: '" + options.mode + \ "'! Using 'cofactor' as default!") self.__mode = marduk_utils.Modes.COFACTOR options.mode = "COFACTOR" if options.language == None: self.println("WARNING: No language specified! Using 'blif' as default!") self.__language = marduk_utils.Languages.BLIF elif options.language.lower() == "blif": self.__language = marduk_utils.Languages.BLIF elif options.language.lower() == "verilog": self.__language = marduk_utils.Languages.VERILOG elif options.language.lower() == "hif": self.__language = marduk_utils.Languages.HIF else: self.println("WARNING: Unknown language: '" + options.language + \ "'! Using 'blif' as default!") self.__language = marduk_utils.Languages.BLIF options.language = "BLIF" if options.transfer_functions and not self.__mode in (marduk_utils.Modes.COFACTOR, marduk_utils.Modes.OLD): self.println("WARNING: Cannot use 'transfer' option in '%s' mode. Will be ignored!" % options.mode) options.transfer_functions = False # Handle options concerning IrrSOP (cache) try: self.__num_class_vectors = int(options.num_class_vectors) except ValueError: self.println("WARNING: Given number of class-vectors '" + options.num_class_vectors + "' is not an integer.") self.println(" Using '1024' as default!") self.__num_class_vectors = 1024 try: self.__update_sigs_size = int(options.update_sigs_size) except ValueError: self.println("WARNING: Given number for update signatures '" + options.update_sigs_size + "' is not an integer.") self.println(" Using '32' as default!") self.__update_sigs_size = 32 try: self.__cache_size = int(options.cache_size) except TypeError: if not options.cache_size: self.__cache_size = None else: raise Exception("Unexpected TypeError for argument --cache-size='%s'." % options.cache_size) except ValueError: self.println("WARNING: Given cache size '" + options.cache_size + "' is not an integer.") self.println(" Using 'None' (=no limit) as default!") self.__cache_size = None self.__dont_care_upper_bound = options.dont_care_upper_bound # The following are not handled yet! self.__check_combinations = options.check_combinations # Missing: option to set threshold for signature update if options.partition == None: self.__partition = None elif options.partition.lower() == "threshold": self.__partition = "Threshold" elif options.partition.lower() == "monolithic": self.__partition = "Monolithic" elif options.partition.lower() == "Iwls95CP".lower(): self.__partition = "Iwls95CP" else: self.println("WARNING: Unknown partition method: '%s'! Using 'None' as default!" % options.partition) self.__partition = None try: self.__verbose = int(options.verbose) except ValueError: self.println("WARNING: Given verbose level '" + options.verbose + "' is not an integer.") self.println(" Using '0' as default!") self.__verbose = 0 if options.dac_recur_limit and options.dac04: try: self.__dac_recur_limit = int(options.dac_recur_limit) except ValueError: self.println("WARNING: Given dac-recur-limit '" + options.verbose + "' is not an integer.") self.println(" Using '5' as default!") self.__dac_recur_limit = 5 else: self.__dac_recur_limit = None if options.dac_call_limit and options.dac04: try: self.__dac_call_limit = int(options.dac_call_limit) except ValueError: self.println("WARNING: Given dac-call-limit '" + options.verbose + "' is not an integer.") self.println(" Using '10' as default!") self.__dac_call_limit = 10 else: self.__dac_call_limit = None if options.dac_search_mode and options.dac04: if options.dac_search_mode.lower() in ['dfs', 'bfs']: self.__dac_search_mode = options.dac_search_mode else: self.println("WARNING: Given dac-search-mode '%s' is unknown." % options.dac_search_mode) self.println(" Using 'bfs' as default.") else: if options.dac04: self.println("WARNING: No dac-search-mode given. Using 'dfs' as default.") self.__dac_search_mode = 'dfs' else: self.__dac_search_mode = None if (not options.dac04) and options.dac_search_mode != None: self.println("WARNING: Specified a dac-search-mode, but '--dac04' was not given.") self.println(" The given mode will be ignored.") if (options.dac_recur_limit and (not options.dac04 or self.__dac_search_mode != 'dfs')): self.println("WARNING: A dac-recur-limit was given, but program is not in DAC04-DFS mode.") self.println(" The given limit will be ignored.") if (options.dac_call_limit and (not options.dac04 or self.__dac_search_mode != 'bfs')): self.println("WARNING: A dac-call-limit was given, but program is not in DAC04-BFS mode.") self.println(" The given limit will be ignored.") if options.dac04: if self.__dac_search_mode == 'dfs' and self.__dac_recur_limit == None: self.println("WARNING: Using DAC'04 in DFS mode without a recursion limit!") self.println(" Run time and memory consumption might become very high!") if self.__dac_search_mode == 'bfs' and self.__dac_call_limit == None: self.println("WARNING: Using DAC'04 in BFS mode without a call limit!") self.println(" Run time and memory consumption might become very high!") # Propagate necessary options to NuSMV from nusmv import opt from nusmv import dd from nusmv import game import re opt.set_batch(opt.OptsHandler_get_instance()) opt.set_verbose_level(opt.OptsHandler_get_instance(), self.__verbose) print "marduk game mode: ", game.opt_game_game(opt.OptsHandler_get_instance()) game.unset_game_game(opt.OptsHandler_get_instance()) print "marduk game mode: ", game.opt_game_game(opt.OptsHandler_get_instance()) # Dealing with reorder method for forced reorderings reorder_method = options.reorder_method.upper() reorder_method = re.sub('CUDD_','',reorder_method) reorder_method = re.sub('REORDER_','',reorder_method) reorder_method = re.sub('_CONVERGE','_CONV',reorder_method) self.__reorder_method = -1 if reorder_method == "SAME": self.__reorder_method = dd.REORDER_SAME elif reorder_method == "NONE": self.__reorder_method = dd.REORDER_NONE elif reorder_method == "RANDOM": self.__reorder_method = dd.REORDER_RANDOM elif reorder_method == "RANDOM_PIVOT": self.__reorder_method = dd.REORDER_RANDOM_PIVOT elif reorder_method == "SIFT": self.__reorder_method = dd.REORDER_SIFT elif reorder_method == "SIFT_CONV": self.__reorder_method = dd.REORDER_SIFT_CONV elif reorder_method == "SYMM_SIFT": self.__reorder_method = dd.REORDER_SYMM_SIFT elif reorder_method == "SYMM_SIFT_CONV": self.__reorder_method = dd.REORDER_SYMM_SIFT_CONV elif reorder_method == "WINDOW2": self.__reorder_method = dd.REORDER_WINDOW2 elif reorder_method == "WINDOW3": self.__reorder_method = dd.REORDER_WINDOW3 elif reorder_method == "WINDOW4": self.__reorder_method = dd.REORDER_WINDOW4 elif reorder_method == "WINDOW2_CONV": self.__reorder_method = dd.REORDER_WINDOW2_CONV elif reorder_method == "WINDOW3_CONV": self.__reorder_method = dd.REORDER_WINDOW3_CONV elif reorder_method == "WINDOW4_CONV": self.__reorder_method = dd.REORDER_WINDOW4_CONV elif reorder_method == "GROUP_SIFT": self.__reorder_method = dd.REORDER_GROUP_SIFT elif reorder_method == "GROUP_SIFT_CONV": self.__reorder_method = dd.REORDER_GROUP_SIFT_CONV elif reorder_method == "ANNEALING": self.__reorder_method = dd.REORDER_ANNEALING elif reorder_method == "GENETIC": self.__reorder_method = dd.REORDER_GENETIC elif reorder_method == "LINEAR": self.__reorder_method = dd.REORDER_LINEAR elif reorder_method == "LINEAR_CONV": self.__reorder_method = dd.REORDER_LINEAR_CONVERGE elif reorder_method == "EXACT": self.__reorder_method = dd.REORDER_EXACT else: self.println("WARNING: Specified unknown reordering method '%s'! Using NONE as default instead." % options.reorder_method) self.__reorder_method = dd.REORDER_NONE # Dealing with reorder method for dynamic reorderings dyn_reorder_method = options.dyn_reorder_method.upper() dyn_reorder_method = re.sub('CUDD_','',dyn_reorder_method) dyn_reorder_method = re.sub('REORDER_','',dyn_reorder_method) dyn_reorder_method = re.sub('_CONVERGE','_CONV',dyn_reorder_method) self.__dyn_reorder_method = -1 if dyn_reorder_method == "SAME": self.__dyn_reorder_method = dd.REORDER_SAME elif dyn_reorder_method == "NONE": self.__dyn_reorder_method = dd.REORDER_NONE elif dyn_reorder_method == "RANDOM": self.__dyn_reorder_method = dd.REORDER_RANDOM elif dyn_reorder_method == "RANDOM_PIVOT": self.__dyn_reorder_method = dd.REORDER_RANDOM_PIVOT elif dyn_reorder_method == "SIFT": self.__dyn_reorder_method = dd.REORDER_SIFT elif dyn_reorder_method == "SIFT_CONV": self.__dyn_reorder_method = dd.REORDER_SIFT_CONV elif dyn_reorder_method == "SYMM_SIFT": self.__dyn_reorder_method = dd.REORDER_SYMM_SIFT elif dyn_reorder_method == "SYMM_SIFT_CONV": self.__dyn_reorder_method = dd.REORDER_SYMM_SIFT_CONV elif dyn_reorder_method == "WINDOW2": self.__dyn_reorder_method = dd.REORDER_WINDOW2 elif dyn_reorder_method == "WINDOW3": self.__dyn_reorder_method = dd.REORDER_WINDOW3 elif dyn_reorder_method == "WINDOW4": self.__dyn_reorder_method = dd.REORDER_WINDOW4 elif dyn_reorder_method == "WINDOW2_CONV": self.__dyn_reorder_method = dd.REORDER_WINDOW2_CONV elif dyn_reorder_method == "WINDOW3_CONV": self.__dyn_reorder_method = dd.REORDER_WINDOW3_CONV elif dyn_reorder_method == "WINDOW4_CONV": self.__dyn_reorder_method = dd.REORDER_WINDOW4_CONV elif dyn_reorder_method == "GROUP_SIFT": self.__dyn_reorder_method = dd.REORDER_GROUP_SIFT elif dyn_reorder_method == "GROUP_SIFT_CONV": self.__dyn_reorder_method = dd.REORDER_GROUP_SIFT_CONV elif dyn_reorder_method == "ANNEALING": self.__dyn_reorder_method = dd.REORDER_ANNEALING elif dyn_reorder_method == "GENETIC": self.__dyn_reorder_method = dd.REORDER_GENETIC elif dyn_reorder_method == "LINEAR": self.__dyn_reorder_method = dd.REORDER_LINEAR elif dyn_reorder_method == "LINEAR_CONV": self.__dyn_reorder_method = dd.REORDER_LINEAR_CONVERGE elif dyn_reorder_method == "EXACT": self.__dyn_reorder_method = dd.REORDER_EXACT else: self.println("WARNING: Specified unknown dynamic reordering method '%s'! Using NONE as default instead." % options.dyn_reorder_method) self.__dyn_reorder_method = dd.REORDER_NONE if options.dyn_reorder: dd.dd_autodyn_enable(dd.cvar.dd_manager, self.__dyn_reorder_method) dd.dd_autodyn_enable(self.__dd_manager, self.__dyn_reorder_method) else: dd.dd_autodyn_disable(dd.cvar.dd_manager) dd.dd_autodyn_disable(self.__dd_manager) if self.__verbose > 0: # FIXXME: Format this output more nicely self.println("Used the following command line options/parameters:") self.println(str(options) + " " + str(self.__args)) # "Declare" attributes, initialize them to empty values. self.__specification = None self.__winning_region = None self.__strategy = None self.__output_functions = None self.__code_generator = None self.__variables = [] ###################################################################################### ###################################################################################### # # Access functions to various members/properties def get_verbose(self): return self.__verbose verbose = property(get_verbose) def get_dyn_reorder(self): return self.__options.dyn_reorder dyn_reorder = property(get_dyn_reorder) def get_mode(self): return self.__mode mode = property(get_mode) def get_language(self): return self.__language language = property(get_language) def get_cache_size(self): return self.__cache_size cache_size = property(get_cache_size) def get_update_sigs_size(self): return self.__update_sigs_size update_sigs_size = property(get_update_sigs_size) def get_num_class_vectors(self): return self.__num_class_vectors num_class_vectors = property(get_num_class_vectors) def get_dont_care_upper_bound(self): return self.__dont_care_upper_bound dont_care_upper_bound = property(get_dont_care_upper_bound) def get_check_combinations(self): return self.__check_combinations check_combinations = property(get_check_combinations) def get_partition(self): return self.__partition partition = property(get_partition) def get_var_order(self): return self.__options.var_order var_order = property(get_var_order) def get_reorder1(self): return self.__options.reorder1 reorder1 = property(get_reorder1) r1 = reorder1 def get_reorder2(self): return self.__options.reorder2 reorder2 = property(get_reorder2) r2 = reorder2 def get_kill(self): return self.__options.kill kill = property(get_kill) def get_transfer_functions(self): return self.__options.transfer_functions transfer_functions = property(get_transfer_functions) def get_dac04(self): return self.__options.dac04 dac04 = property(get_dac04) def get_dac_recur_limit(self): return self.__dac_recur_limit dac_recur_limit = property(get_dac_recur_limit) def get_dac_call_limit(self): return self.__dac_call_limit dac_call_limit = property(get_dac_call_limit) def get_dac_search_mode(self): return self.__dac_search_mode dac_search_mode = property(get_dac_search_mode) def get_reorder_method(self): return self.__reorder_method reorder_method = property(get_reorder_method) def get_dyn_reorder_method(self): return self.__dyn_reorder_method dyn_reorder_method = property(get_dyn_reorder_method) def get_one_hot(self): return self.__options.one_hot one_hot = property(get_one_hot) def get_variables(self): return self.__variables[:] def set_variables(self, variables): self.__variables = variables[:] vars = property(get_variables, set_variables) def get_input_variables(self): return [var for var in self.__variables if var.type == marduk_utils.VariableType.INPUT] input_vars = property(get_input_variables) def get_output_variables(self): return [var for var in self.__variables if var.type == marduk_utils.VariableType.OUTPUT] output_vars = property(get_output_variables) def get_state_variables(self): return [var for var in self.__variables if var.type == marduk_utils.VariableType.STATE] state_vars = property(get_state_variables) def get_input_file_name(self): return self.__input_file input_file = property(get_input_file_name) def get_output_file_name(self): return self.__output_file output_file = property(get_output_file_name) def get_debug_mode(self): return self.__options.debug_mode debug_mode = property(get_debug_mode) def get_winning_region(self): return self.__winning_region def set_winning_region(self, winning_region): self.__winning_region = winning_region winning_region = property(get_winning_region, set_winning_region) def get_specification(self): return self.__specification def set_specification(self, specification): self.__specification = specification specification = property(get_specification, set_specification) def get_dd_manager(self): return self.__dd_manager dd_mgr = property(get_dd_manager) def add_variable(self, var): """ Adds the given variable to the internal list of variables. This function can be used to add variables, which are not part of the initial specification, e.g. the state (jx) variables. If a variable with the given name already exists, an exception is thrown. """ if var.name in [variable.name for variable in self.__variables]: raise MardukException(("Error: A variable with name '%s' already exists!" % var.name)) self.__variables.append(var) def print_welcome_message(): print "------------------------------------------------------" print " Welcome to Marduk - The God who slaughtered Anzu! " print "------------------------------------------------------\n" print_welcome_message = staticmethod(print_welcome_message) def run(self): from nusmv import dd import marduk_utils import time start_wall_clock = time.clock() self.println("------------------------------------------------------") self.println("Synthesize " + str(self.input_file) + " to " + str(self.output_file)) if self.__options.mode != None: self.println("Mode: " + self.__options.mode.upper()) if self.__options.language != None: self.println("Output Language: " + self.__options.language.upper()) from datetime import datetime self.println("Start time: %s" % datetime.now().ctime()) #print "Reordering status:", dd.dd_reordering_status(dd.cvar.dd_manager) self.println("\n Dynamic reordering \t\t\t\t\t" + str(self.dyn_reorder)) self.println(" Reordering method for dynamic reordering \t\t" + marduk_utils.reorder_method_to_string(self.dyn_reorder_method)) self.println(" Reordering method for forced reordering \t\t" + marduk_utils.reorder_method_to_string(self.reorder_method)) self.println(" Reorder BDD after reading configuration \t\t" + str(self.r1)) self.println(" Reorder BDD after generating output functions \t" + str(self.r2)) self.println(" Kill strategy and reorder afterwards \t\t" + str(self.kill)) self.println(" One-hot encoding of jx vars\t\t\t\t" + str(self.one_hot)) self.println(" Transfer functions to new DD manager and reorder \t" + str(self.transfer_functions)) self.println(" Generate functions according to\n Baneres, Cortadella, Kishinevsky (DAC'04)\t\t" + str(self.dac04)) if self.dac04: if self.dac_search_mode == 'dfs': self.println(" DAC'04 search mode \t\t\t\t\tDepth First") self.println(" DAC'04 recursion depth limit\t\t\t\t" + str(self.dac_recur_limit)) elif self.dac_search_mode == 'bfs': self.println(" DAC'04 search mode \t\t\t\t\tBreadth First") self.println(" DAC'04 call limit\t\t\t\t\t" + str(self.dac_call_limit)) if self.__mode in (marduk_utils.Modes.IRRSOP, marduk_utils.Modes.FACTOR): if self.cache_size != None: self.println(" Function cache size \t\t\t\t\t" + str(self.cache_size)) else: self.println(" Function cache size \t\t\t\t\tunlimited") self.println(" Number of class vectors \t\t\t\t" + str(self.num_class_vectors)) self.println(" Number of new vectors for cache reorganization \t" + str(self.update_sigs_size)) self.println(" Use don't care upper bound for ISoP_d \t\t\t" + str(self.dont_care_upper_bound)) self.println(" Check combinations of 2 functions \t\t\t" + str(self.check_combinations)) self._starttime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println("\n Timing Information:") # Load specification from input file self.__specification = Specification(self) self.__specification.readSpecification() self._specificationtime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Specification read within\t\t\t %7.2f seconds" %(self._specificationtime-self._starttime)) # Create list of variables and store it here in main class, for central access self.__variables = self.__specification.create_variable_list() if self.verbose > 1: print "Length of variables list: ", len(self.__variables) for var in self.__variables: print var if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) if self.var_order: self.println(' Forcing variable order: ' + self.var_order) marduk_utils.set_variable_ordering(self.var_order, self.vars, self.dd_mgr) if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) # Compute winning region import sys self.__winning_region = WinningRegion(self, self.__specification) self.__winning_region.calcWinningRegion() self._winningregiontime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Compute winning region within \t\t %7.2f seconds" %(self._winningregiontime - self._reorder1time)) if(not(self.__winning_region.isRealizable())): self.println("The given specification is NOT REALIZABLE!\n") if not self.debug_mode: self.println("Use the argument --dm to debug unrealizability\n") return del self.__winning_region self.__spec_debugger = SpecDebugger(self) self.__spec_debugger.debug(self.debug_mode) return self.__winning_region_size = self.__winning_region.winRegion.size # Compute strategy self.__strategy = Strategy(self) self.__strategy.calcStrategy() self._strategytime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Compute strategy within \t\t\t %7.2f seconds" %(self._strategytime-self._winningregiontime)) from marduk_utils import VariableType self.println("\nStrategy Characterization:") input_vars = [var.ns for var in self.input_vars] + [var.ps for var in self.vars] output_vars = [var.ns for var in self.vars if var.type != VariableType.INPUT] begin = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime strat_char = marduk_utils.characterize_relation(self.__strategy.strategy_bdd, input_vars, output_vars, return_bdds=True) char_time = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime - begin self.println("(computed in %7.2f seconds)" % char_time) total = 2 ** strat_char['num_inputs'] defined = (strat_char['num_defined'] / total) * 100 fixed = (strat_char['num_fixed'] / total) * 100 dc = (strat_char['num_dc'] / total) * 100 non_dc = (strat_char['num_non_dc'] / total) * 100 self.println("Defined:%7.2f%%" % defined) self.println("Fixed:\t %7.2f%%" % fixed) self.println("DC:\t %7.2f%%" % dc) self.println("Non-DC:\t %7.2f%%\n" % non_dc) strat_dc = strat_char['dc_bdd'] del strat_char if self.__mode in (marduk_utils.Modes.COFACTOR, marduk_utils.Modes.OLD): self.do_cofactor_mode() else: self.do_function_generator_mode(strat_dc=strat_dc) if self.transfer_functions: self.println(" Transferred output functions within \t\t %7.2f seconds" % self.__code_generator._transfer_time) self.println(" Reordering transferred BDD took \t\t %7.2f seconds" % self.__code_generator._reorder_time) self.println("\n Results in needed overall time of\t\t %7.2f seconds \n" %(self._codegentime - self._starttime)) stop_wall_clock_time = time.clock() self.println(" Overall wall clock time\t\t\t %7.2f seconds" % (stop_wall_clock_time - start_wall_clock)) self.println("\n BDD-Size Information:") self.println(" Size of Winning Region: \t\t %10d bdd-nodes" % self.__winning_region_size) self.println(" Size of rho1 is \t\t\t %10d bdd-nodes" %self.__strategy.rho1_size) self.println(" Size of rho2 is \t\t\t %10d bdd-nodes" %self.__strategy.rho2_size) self.println(" Size of rho3 is \t\t\t %10d bdd-nodes" %self.__strategy.rho3_size) self.println("------------------------------------------------------") self.println(" FINISHED synthesis!" ) self.println("------------------------------------------------------") if self.verbose > 1: print "Current Ordering:" print marduk_utils.print_variable_ordering(self.vars) # The following should be the last block in the run method: self.__code_generator.append_comment(self.__printed_lines) # Killing references to other objects, break up circular references self.__specification = None self.__winning_region = None self.__strategy = None self.__output_functions = None self.__code_generator = None #------------------------------------------------------------------------------------------------------------------- def do_cofactor_mode(self): # Compute output functions self.__output_functions = OutputFunctions(self, self.__winning_region, self.__strategy) if not self.dac04: #new_rel = self.__output_functions.constructFunctions() else: new_rel = self.__output_functions.constructFunctionsDAC04(recur_limit=self.dac_recur_limit, call_limit=self.dac_call_limit, bfs=(self.dac_search_mode == 'bfs')) self._size_gen_strat = None # new_rel.size self._outputfcttime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self._reorder2time = self._outputfcttime self._killingtime = self._outputfcttime self.println(" Compute output functions within \t\t %7.2f seconds" %(self._outputfcttime - self._strategytime)) if self.reorder2: result = dd.dd_reorder(self.__dd_manager, self.reorder_method,0) self._reorder2time = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Second reordering of bdd takes \t\t %7.2f seconds" %(self._reorder2time - self._outputfcttime)) if self.kill: self.__strategy.killStrategy() dd.dd_reorder(self.__dd_manager, self.reorder_method,0) self._killingtime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Killing strat and reordering takes\t\t %7.2f seconds" %(self._killingtime - self._reorder2time)) before_code_gen = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime if not self.mode == marduk_utils.Modes.OLD: if self.language == marduk_utils.Languages.BLIF: self.__code_generator = BlifFromGatesGenerator(self.output_file) elif self.language == marduk_utils.Languages.HIF: self.__code_generator = HifGenerator(self.output_file) elif self.language == marduk_utils.Languages.VERILOG: self.__code_generator = VerilogGenerator(self.output_file) dd.dd_autodyn_disable(dd.cvar.dd_manager) dd.dd_autodyn_disable(self.__dd_manager) self.__code_generator.convert_functions_to_gates(self, self.__output_functions, self.__dd_manager) else: self.__code_generator = BlifGenerator(self, self.__output_functions) self.__code_generator.write_code_to_file() self._codegentime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Code generation takes \t\t\t %7.2f seconds" %(self._codegentime - before_code_gen)) return #--------------------------------------------------------------------------------------------------------------------- def do_function_generator_mode(self, strat_dc=None): if self.dac04: raise MardukException("ERROR! DAC'04 function generation is not implemented (yet) for function-generator mode!") # Compute output functions import sys self.__output_functions = OutputFunctions(self, self.__winning_region, self.__strategy) self.__code_generator = self.__output_functions.constructFunctionsUsingGenerator(strat_dc=strat_dc) self._outputfcttime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Compute output functions within \t\t %7.2f seconds" %(self._outputfcttime - self._strategytime)) if self.reorder2: self.println("WARNING: Using --reorder2 in IrrSOP mode just takes time and has no merit.") result = dd.dd_reorder(self.__dd_manager, self.reorder_method,0) self._reorder2time = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Second reordering of bdd takes \t\t %7.2f seconds" %(self._reorder2time - self._outputfcttime)) self._reorder2time = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime if self.kill: self.println("WARNING: Using --kill in IrrSOP mode just takes time and has no merit." ) self.__strategy.killStrategy() dd.dd_reorder(self.__dd_manager, self.reorder_method,0) self._killingtime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime self.println(" Killing strat and reordering takes\t\t %7.2f seconds" %(self._killingtime - self._reorder2time)) self.__code_generator.write_code_to_file() self._codegentime = resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_SELF).ru_stime return def println(self, line): print line self.__printed_lines.append(line)
def setUp(self): from decimal import Decimal # noqa self.spec = Specification("x == Decimal('5.4')", candidate_name='x')
class BlacKnightClient(object): """ The client that runs on every host in a BlacKnight-based appliance. This class wraps the ZooKeeper client around which BlacKnight is built. The clients are perpetually engaged in a leader election which is initiaited by the :func:`run` method. The current leader is responsible for appliance monitoring and remediation. When a leader is elected it calls the :func:`lead`. """ # ZooKeeper paths zk_path = '/blacknight' spec_znode = zk_path + '/spec.yaml' elect_path = zk_path + '/elect' lock_path = zk_path + '/lock' barrier_path = zk_path + '/barrier' ensemble_path = zk_path + '/ensemble' services_path = zk_path + '/services' unused_hosts_path = services_path + '/unused_hosts' hosts_path = zk_path + '/hosts' args_path = zk_path + '/args' def __init__(self, port): # TODO: figure out why this docstring disappears """ Initialize a client to run on a host. :param port: port on which local ZooKeeper server is running :type: port: string """ self.logger = logging.getLogger('blacknight.client') self.local_zk = 'localhost' + ':' + str(port) # Start the ZK client self.logger.debug('Connecting to ZooKeeper at {}'.format(self.local_zk)) self.client = KazooClient(self.local_zk) self.client.start() # Load the appliance specification # TODO: handle no spec in ZooKeeper (barrier before run?) @self.client.DataWatch(BlacKnightClient.spec_znode) def watch_spec(data, stat): if data: self.logger.debug('Reloading spec.yaml') self.spec = Specification(data) self.logger.debug('Parsed specification') else: self.spec = None self.logger.warn('Specification not found in {}'.format(BlacKnightClient.spec_znode)) # Get synchronization info self.election = self.client.Election(BlacKnightClient.elect_path) self.lock = self.client.Lock(BlacKnightClient.lock_path) self.barrier = self.client.Barrier(BlacKnightClient.barrier_path) def run(self): """ Add this client to the election. After calling this method, the client should either be the leader or blocked in the election. """ # TODO: check client and spec before running self.logger.debug('Joining election') self.election.run(self.lead) def lead(self): """ Called when this client is elected leader. This function monitors the appliance by registering a watcher on the *services* node. Each service keeps an ephemeral WRITEME """ self.logger.debug('Elected leader') self.client.ensure_path(BlacKnightClient.args_path) self.client.ensure_path(BlacKnightClient.services_path) # TODO: should we set allow_session_lost=True for this watcher? @self.client.ChildrenWatch(BlacKnightClient.services_path) def watch_services(children): # Perform remediation under lock in case a watcher from a previous # leader hasn't been cleared for some reason with self.lock: self.logger.debug('Detected change') # TODO: find a better way to wait for ephemeral nodes to vanish sleep(2) # TODO: reconfigure ZooKeeper # ensemble = self._client.get_children(event.path) # ensemble = reduce(lambda a, b: a + ',' + b, ensemble) # self._client.reconfig('', '', ensemble) # Query appliance state and remediate if necessary services, args = self.query() actions = self.spec.diff(services) self.logger.debug('Actions: {}'.format(actions)) for action in actions: action.run(services, args) # The barrier must be reset every time the watcher is triggered self.barrier.remove() # Wait behind a barrier for a watcher to be triggered while True: self.barrier.create() self.barrier.wait() def query(self): """ Retrieve the current state of the appliance. This method surveys the ``/blacknight/service/`` znode to construct the current applinace state. The services are returned as a dictionary mapping each role to a list of services. The arguments (global configuration parameters such as secret keys) are a returned as a dictionary mapping each argument key to its value. :return: services, args :rtype: Tuple({string: []}, {string: string}) """ services = {} children = self.client.get_children(BlacKnightClient.services_path) for role in children: role_path = BlacKnightClient.services_path + '/' + role services[role] = self.client.get_children(role_path) # Get current appliance configuration args = {} children = self.client.get_children(BlacKnightClient.args_path) for arg in children: path = BlacKnightClient.args_path + '/' + arg value, stat = self.client.get(path) args[arg] = value return services, args