def generateSwarmParams(stats):
  """ Generate parameters for creating a model

  :param stats: dict with "min", "max" and optional "minResolution"; values must
    be integer, float or None.

  :returns: if either minVal or maxVal is None, returns None; otherwise returns
    swarmParams object that is suitable for passing to startMonitoring and
    startModel
  """
  minVal = stats.get("min")
  maxVal = stats.get("max")
  minResolution = stats.get("minResolution")
  if minVal is None or maxVal is None:
    return None

  # Create possible swarm parameters based on metric data
  possibleModels = getScalarMetricWithTimeOfDayParams(
    metricData=[0],
    minVal=minVal,
    maxVal=maxVal,
    minResolution=minResolution)

  swarmParams = possibleModels[0]

  swarmParams["inputRecordSchema"] = (
    fieldmeta.FieldMetaInfo("c0", fieldmeta.FieldMetaType.datetime,
                            fieldmeta.FieldMetaSpecial.timestamp),
    fieldmeta.FieldMetaInfo("c1", fieldmeta.FieldMetaType.float,
                            fieldmeta.FieldMetaSpecial.none),
  )

  return swarmParams
Beispiel #2
0
def generateSwarmParams(stats):
    """ Generate parameters for creating a model

  :param stats: dict with "min", "max" and optional "minResolution"; values must
    be integer, float or None.

  :returns: if either minVal or maxVal is None, returns None; otherwise returns
    swarmParams object that is suitable for passing to startMonitoring and
    startModel
  """
    minVal = stats.get("min")
    maxVal = stats.get("max")
    minResolution = stats.get("minResolution")
    if minVal is None or maxVal is None:
        return None

    # Create possible swarm parameters based on metric data
    possibleModels = getScalarMetricWithTimeOfDayParams(
        metricData=[0],
        minVal=minVal,
        maxVal=maxVal,
        minResolution=minResolution)

    swarmParams = possibleModels[0]

    swarmParams["inputRecordSchema"] = (
        fieldmeta.FieldMetaInfo("c0", fieldmeta.FieldMetaType.datetime,
                                fieldmeta.FieldMetaSpecial.timestamp),
        fieldmeta.FieldMetaInfo("c1", fieldmeta.FieldMetaType.float,
                                fieldmeta.FieldMetaSpecial.none),
    )

    return swarmParams
Beispiel #3
0
  def _createModel(cls, stats):
    """Instantiate and configure an OPF model

    :param dict stats: Metric data stats per stats_schema.json in the
      unicorn_backend package.
    :returns: OPF Model instance
    """

    # TODO remove the "DummyModel" code path once the ILLEGAL INSTRUCTION issue
    # in nupic is resolved;
    # Create a dummy model instead of a real one temporarily, while we're
    # having trouble with the latest nupic builds on the Mac OS Yosemite that
    # result in ILLEGAL INSTRUCTION in nupic.bindings. This is good enough for
    # now to enable FrontEnd development.
    if False:
      class DummyModel(object):
        class Result(object):
          def __init__(self, inferences):
            self.inferences = inferences

        def run(self, inputRecord):
          inputRecord = inputRecord
          return self.Result(dict(anomalyScore=0.9999))

      return DummyModel()

    else:
      # THIS IS THE CORRECT PRODUCTION CODE that is failing with ILLEGAL
      # INSTRUCTION in  ModelFactory.create on my Mac OS Yosemite laptop.

      # Generate swarm params
      possibleModels = getScalarMetricWithTimeOfDayParams(
        metricData=[0],
        minVal=stats["min"],
        maxVal=stats["max"],
        minResolution=stats.get("minResolution"))

      swarmParams = possibleModels[0]

      model = ModelFactory.create(modelConfig=swarmParams["modelConfig"])
      model.enableLearning()
      model.enableInference(swarmParams["inferenceArgs"])

      return model
  def _createModel(cls, stats):
    """Instantiate and configure an OPF model

    :param dict stats: Metric data stats per stats_schema.json in the
      unicorn_backend package.
    :returns: OPF Model instance
    """
    # Generate swarm params
    possibleModels = getScalarMetricWithTimeOfDayParams(
      metricData=[0],
      minVal=stats["min"],
      maxVal=stats["max"],
      minResolution=stats.get("minResolution"))

    swarmParams = possibleModels[0]

    model = ModelFactory.create(modelConfig=swarmParams["modelConfig"])
    model.enableLearning()
    model.enableInference(swarmParams["inferenceArgs"])
Beispiel #5
0
  def _createModel(cls, stats):
    """Instantiate and configure an OPF model

    :param dict stats: Metric data stats per stats_schema.json in the
      unicorn_backend package.
    :returns: OPF Model instance
    """
    # Generate swarm params
    possibleModels = getScalarMetricWithTimeOfDayParams(
      metricData=[0],
      minVal=stats["min"],
      maxVal=stats["max"],
      minResolution=stats.get("minResolution"))

    swarmParams = possibleModels[0]

    model = ModelFactory.create(modelConfig=swarmParams["modelConfig"])
    model.enableLearning()
    model.enableInference(swarmParams["inferenceArgs"])

    return model
Beispiel #6
0
  def testModelParams(self):
    """
    Test that clusterParams loads returns a valid dict that can be instantiated
    as a CLAModel.
    """
    params = getScalarMetricWithTimeOfDayParams([0],
      minVal=23.42, maxVal=23.420001)[0]

    encodersDict= (
      params['modelConfig']['modelParams']['sensorParams']['encoders']
      )

    model = ModelFactory.create(modelConfig=params['modelConfig'])
    self.assertIsInstance(model, CLAModel,
                      "JSON returned cannot be used to create a model")

    # Ensure we have a time of day field
    self.assertIsNotNone(encodersDict['c0_timeOfDay'])

    # Ensure resolution doesn't get too low
    if (encodersDict['c1']['type'] == 'RandomDistributedScalarEncoder'):
      self.assertGreaterEqual(encodersDict['c1']['resolution'], 0.001,
                  "Resolution is too low")
    def testModelSwapper(self):
        """Simple end-to-end test of the model swapper system."""

        modelSchedulerSubprocess = self._startModelSchedulerSubprocess()
        self.addCleanup(lambda: modelSchedulerSubprocess.kill() if
                        modelSchedulerSubprocess.returncode is None else None)

        modelID = "foobar"
        resultBatches = []

        with ModelSwapperInterface() as swapperAPI:
            possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0],
                                                                minVal=0,
                                                                maxVal=1000)

            # Submit requests including a model creation command and two data rows.
            args = possibleModels[0]
            args["inputRecordSchema"] = (
                FieldMetaInfo("c0", FieldMetaType.datetime,
                              FieldMetaSpecial.timestamp),
                FieldMetaInfo("c1", FieldMetaType.float,
                              FieldMetaSpecial.none),
            )

            # Define the model
            _LOGGER.info("Defining the model")
            swapperAPI.defineModel(modelID=modelID,
                                   args=args,
                                   commandID="defineModelCmd1")

            # Attempt to define the same model again
            _LOGGER.info("Defining the model again")
            swapperAPI.defineModel(modelID=modelID,
                                   args=args,
                                   commandID="defineModelCmd2")

            # Send input rows to the model
            inputRows = [
                ModelInputRow(
                    rowID="rowfoo",
                    data=[datetime.datetime(2013, 5, 23, 8, 13, 00), 5.3]),
                ModelInputRow(
                    rowID="rowbar",
                    data=[datetime.datetime(2013, 5, 23, 8, 13, 15), 2.4]),
            ]
            _LOGGER.info("Submitting batch of %d input rows...",
                         len(inputRows))
            swapperAPI.submitRequests(modelID=modelID, requests=inputRows)

            _LOGGER.info("These models have pending input: %s",
                         swapperAPI.getModelsWithInputPending())

            # Retrieve all results.
            # NOTE: We collect results via background thread to avoid
            # deadlocking the test runner in the event consuming blocks unexpectedly
            _LOGGER.info("Reading all batches of results...")

            numBatchesExpected = 3
            resultBatches.extend(
                self._consumeResults(numBatchesExpected, timeout=20))

            self.assertEqual(len(resultBatches), numBatchesExpected)

            with MessageBusConnector() as bus:
                # The results message queue should be empty now
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

            # Delete the model
            _LOGGER.info("Deleting the model")
            swapperAPI.deleteModel(modelID=modelID,
                                   commandID="deleteModelCmd1")

            _LOGGER.info("Waiting for model deletion result")
            resultBatches.extend(self._consumeResults(1, timeout=20))

            self.assertEqual(len(resultBatches), 4)

            with MessageBusConnector() as bus:
                # The results message queue should be empty now
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

                # The model input queue should be deleted now
                self.assertFalse(
                    bus.isMessageQeueuePresent(
                        swapperAPI._getModelInputQName(modelID=modelID)))

            # Try deleting the model again, to make sure there are no exceptions
            _LOGGER.info("Attempting to delete the model again")
            swapperAPI.deleteModel(modelID=modelID,
                                   commandID="deleteModelCmd1")

        # Verify results

        # First result batch should be the first defineModel result
        batch = resultBatches[0]
        self.assertEqual(batch.modelID, modelID)
        self.assertEqual(len(batch.objects), 1)

        result = batch.objects[0]
        self.assertIsInstance(result, ModelCommandResult)
        self.assertEqual(result.method, "defineModel")
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.commandID, "defineModelCmd1")

        # The second result batch should for the second defineModel result for the
        # same model
        batch = resultBatches[1]
        self.assertEqual(batch.modelID, modelID)
        self.assertEqual(len(batch.objects), 1)

        result = batch.objects[0]
        self.assertIsInstance(result, ModelCommandResult)
        self.assertEqual(result.method, "defineModel")
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.commandID, "defineModelCmd2")

        # The third batch should be for the two input rows
        batch = resultBatches[2]
        self.assertEqual(batch.modelID, modelID)
        self.assertEqual(len(batch.objects), len(inputRows))

        for inputRow, result in zip(inputRows, batch.objects):
            self.assertIsInstance(result, ModelInferenceResult)
            self.assertEqual(result.status, htmengineerrno.SUCCESS)
            self.assertEqual(result.rowID, inputRow.rowID)
            self.assertIsInstance(result.anomalyScore, float)

        # The fourth batch should be for the "deleteModel"
        batch = resultBatches[3]
        self.assertEqual(batch.modelID, modelID)
        self.assertEqual(len(batch.objects), 1)

        result = batch.objects[0]
        self.assertIsInstance(result, ModelCommandResult)
        self.assertEqual(result.method, "deleteModel")
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.commandID, "deleteModelCmd1")

        # Signal Model Scheduler Service subprocess to shut down and wait for it
        waitResult = dict()

        def runWaiterThread():
            try:
                waitResult["returnCode"] = modelSchedulerSubprocess.wait()
            except:
                _LOGGER.exception(
                    "Waiting for modelSchedulerSubprocess failed")
                waitResult["exceptionInfo"] = traceback.format_exc()
                raise
            return

        modelSchedulerSubprocess.terminate()
        waiterThread = threading.Thread(target=runWaiterThread)
        waiterThread.setDaemon(True)
        waiterThread.start()
        waiterThread.join(timeout=30)
        self.assertFalse(waiterThread.isAlive())

        self.assertEqual(waitResult["returnCode"], 0, msg=repr(waitResult))
  def testRunModelWithFullThenIncrementalCheckpoints(self):
    # Have model_runner create a full checkpoint, then incremental checkpoint
    modelID = "foobar"

    checkpointMgr = model_checkpoint_mgr.ModelCheckpointMgr()

    with ModelSwapperInterface() as swapperAPI:
      possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0],
                                                         minVal=0,
                                                         maxVal=1000)

      # Submit requests including a model creation command and two data rows.
      args = possibleModels[0]
      args["inputRecordSchema"] = (
          FieldMetaInfo("c0", FieldMetaType.datetime,
                        FieldMetaSpecial.timestamp),
          FieldMetaInfo("c1", FieldMetaType.float,
                        FieldMetaSpecial.none),
      )

      # Define the model
      _LOGGER.info("Defining the model")
      swapperAPI.defineModel(modelID=modelID, args=args,
                             commandID="defineModelCmd1")

      # Send input rows to the model
      inputRows = [
          ModelInputRow(rowID="rowfoo",
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 00), 5.3]),
          ModelInputRow(rowID="rowbar",
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 15), 2.4]),
      ]

      _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...",
                   len(inputRows), inputRows[0].rowID, inputRows[-1].rowID)
      swapperAPI.submitRequests(modelID=modelID, requests=inputRows)

      # Run model_runner and collect results
      with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess:
        resultBatches = self._consumeResults(numExpectedBatches=2, timeout=15)
        self._waitForProcessToStopAndCheck(modelRunnerProcess)

      with MessageBusConnector() as bus:
        # The results message queue should be empty now
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

      self.assertEqual(len(resultBatches), 2, repr(resultBatches))

      # First result batch should be the first defineModel result
      batch = resultBatches[0]
      self.assertEqual(batch.modelID, modelID)
      self.assertEqual(len(batch.objects), 1)

      result = batch.objects[0]
      self.assertIsInstance(result, ModelCommandResult)
      self.assertEqual(result.method, "defineModel")
      self.assertEqual(result.status, htmengineerrno.SUCCESS)
      self.assertEqual(result.commandID, "defineModelCmd1")

      # The second result batch should be for the two input rows
      batch = resultBatches[1]
      self.assertEqual(batch.modelID, modelID)
      self.assertEqual(len(batch.objects), len(inputRows))

      for inputRow, result in zip(inputRows, batch.objects):
        self.assertIsInstance(result, ModelInferenceResult)
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.rowID, inputRow.rowID)
        self.assertIsInstance(result.anomalyScore, float)

      # Verify model checkpoint
      model = checkpointMgr.load(modelID)
      del model

      attrs = checkpointMgr.loadCheckpointAttributes(modelID)
      self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                    attrs, msg=repr(attrs))
      self.assertEqual(
        len(attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME]),
        2, msg=repr(attrs))
      self.assertNotIn(
        model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
        attrs, msg=repr(attrs))

      # Now, check incremental checkpointing
      inputRows2 = [
          ModelInputRow(rowID=2,
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 20), 2.7]),
          ModelInputRow(rowID=3,
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 25), 3.9]),
      ]

      _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...",
                   len(inputRows2), inputRows2[0].rowID, inputRows2[-1].rowID)
      inputBatchID = swapperAPI.submitRequests(modelID=modelID,
                                               requests=inputRows2)

      with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess:
        resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15)
        self._waitForProcessToStopAndCheck(modelRunnerProcess)

      with MessageBusConnector() as bus:
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

      batch = resultBatches[0]
      self.assertEqual(batch.modelID, modelID)
      self.assertEqual(len(batch.objects), len(inputRows2))

      for inputRow, result in zip(inputRows2, batch.objects):
        self.assertIsInstance(result, ModelInferenceResult)
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.rowID, inputRow.rowID)
        self.assertIsInstance(result.anomalyScore, float)

      model = checkpointMgr.load(modelID)
      del model

      attrs = checkpointMgr.loadCheckpointAttributes(modelID)
      self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                    attrs, msg=repr(attrs))
      self.assertSequenceEqual(
        attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME],
        [inputBatchID], msg=repr(attrs))

      self.assertIn(
        model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
        attrs, msg=repr(attrs))

      self.assertSequenceEqual(
        model_runner._ModelArchiver._decodeDataSamples(
          attrs[model_runner._ModelArchiver.
                _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]),
        [row.data for row in inputRows2], msg=repr(attrs))

      # Final run with incremental checkpointing
      inputRows3 = [
          ModelInputRow(rowID=4,
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 30), 4.7]),
          ModelInputRow(rowID=5,
                        data=[datetime.datetime(2014, 5, 23, 8, 13, 35), 5.9]),
      ]

      _LOGGER.info("Submitting batch of %d input rows with ids=[%s..%s]...",
                   len(inputRows3), inputRows3[0].rowID, inputRows3[-1].rowID)
      inputBatchID = swapperAPI.submitRequests(modelID=modelID,
                                               requests=inputRows3)

      with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess:
        resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15)
        self._waitForProcessToStopAndCheck(modelRunnerProcess)

      with MessageBusConnector() as bus:
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

      batch = resultBatches[0]
      self.assertEqual(batch.modelID, modelID)
      self.assertEqual(len(batch.objects), len(inputRows3))

      for inputRow, result in zip(inputRows3, batch.objects):
        self.assertIsInstance(result, ModelInferenceResult)
        self.assertEqual(result.status, htmengineerrno.SUCCESS)
        self.assertEqual(result.rowID, inputRow.rowID)
        self.assertIsInstance(result.anomalyScore, float)

      model = checkpointMgr.load(modelID)
      del model

      attrs = checkpointMgr.loadCheckpointAttributes(modelID)

      self.assertIn(model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                    attrs, msg=repr(attrs))
      self.assertSequenceEqual(
        attrs[model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME],
        [inputBatchID], msg=repr(attrs))

      self.assertIn(
        model_runner._ModelArchiver._INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
        attrs, msg=repr(attrs))
      self.assertSequenceEqual(
        model_runner._ModelArchiver._decodeDataSamples(
          attrs[model_runner._ModelArchiver.
                _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]),
        [row.data for row in itertools.chain(inputRows2, inputRows3)],
        msg=repr(attrs))

      # Delete the model
      _LOGGER.info("Deleting the model=%s", modelID)
      swapperAPI.deleteModel(modelID=modelID, commandID="deleteModelCmd1")

      with self._startModelRunnerSubprocess(modelID) as modelRunnerProcess:
        resultBatches = self._consumeResults(numExpectedBatches=1, timeout=15)
        self._waitForProcessToStopAndCheck(modelRunnerProcess)

      self.assertEqual(len(resultBatches), 1, repr(resultBatches))

      # First result batch should be the first defineModel result
      batch = resultBatches[0]
      self.assertEqual(batch.modelID, modelID)
      self.assertEqual(len(batch.objects), 1)

      result = batch.objects[0]
      self.assertIsInstance(result, ModelCommandResult)
      self.assertEqual(result.method, "deleteModel")
      self.assertEqual(result.status, htmengineerrno.SUCCESS)
      self.assertEqual(result.commandID, "deleteModelCmd1")

      with MessageBusConnector() as bus:
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

        # The model input queue should be deleted now
        self.assertFalse(
          bus.isMessageQeueuePresent(
            swapperAPI._getModelInputQName(modelID=modelID)))

      # The model checkpoint should be gone too
      with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
        checkpointMgr.load(modelID)

      with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
        checkpointMgr.loadModelDefinition(modelID)

      with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
        checkpointMgr.loadCheckpointAttributes(modelID)

      with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
        checkpointMgr.remove(modelID)
  def testCloneModel(self):

    modelSchedulerSubprocess = self._startModelSchedulerSubprocess()
    self.addCleanup(lambda: modelSchedulerSubprocess.kill()
                    if modelSchedulerSubprocess.returncode is None
                    else None)

    modelID = "abc"
    destModelID = "def"

    resultBatches = []

    with ModelSwapperInterface() as swapperAPI:
      possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0],
                                                         minVal=0,
                                                         maxVal=1000)

      # Submit requests including a model creation command and two data rows.
      args = possibleModels[0]
      args["inputRecordSchema"] = (
          FieldMetaInfo("c0", FieldMetaType.datetime,
                        FieldMetaSpecial.timestamp),
          FieldMetaInfo("c1", FieldMetaType.float,
                        FieldMetaSpecial.none),
      )

      # Define the model
      _LOGGER.info("Defining the model")
      swapperAPI.defineModel(modelID=modelID, args=args,
                             commandID="defineModelCmd1")

      resultBatches.extend(self._consumeResults(1, timeout=20))
      self.assertEqual(len(resultBatches), 1)

      # Clone the just-defined model
      _LOGGER.info("Cloning model")
      swapperAPI.cloneModel(modelID, destModelID,
                            commandID="cloneModelCmd1")

      resultBatches.extend(self._consumeResults(1, timeout=20))
      self.assertEqual(len(resultBatches), 2)

      # Send input rows to the clone
      inputRows = [
          ModelInputRow(rowID="rowfoo",
                        data=[datetime.datetime(2013, 5, 23, 8, 13, 00), 5.3]),
          ModelInputRow(rowID="rowbar",
                        data=[datetime.datetime(2013, 5, 23, 8, 13, 15), 2.4]),
      ]
      _LOGGER.info("Submitting batch of %d input rows...", len(inputRows))
      swapperAPI.submitRequests(modelID=destModelID, requests=inputRows)

      _LOGGER.info("These models have pending input: %s",
                   swapperAPI.getModelsWithInputPending())

      resultBatches.extend(self._consumeResults(1, timeout=20))
      self.assertEqual(len(resultBatches), 3)

      with MessageBusConnector() as bus:
        # The results message queue should be empty now
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))


      # Delete the model
      _LOGGER.info("Deleting the model")
      swapperAPI.deleteModel(modelID=destModelID,
                             commandID="deleteModelCmd1")

      _LOGGER.info("Waiting for model deletion result")
      resultBatches.extend(self._consumeResults(1, timeout=20))

      self.assertEqual(len(resultBatches), 4)

      with MessageBusConnector() as bus:
        # The results message queue should be empty now
        self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

        # The model input queue should be deleted now
        self.assertFalse(
          bus.isMessageQeueuePresent(
            swapperAPI._getModelInputQName(modelID=destModelID)))


    # Verify results

    # First result batch should be the defineModel result
    batch = resultBatches[0]
    self.assertEqual(batch.modelID, modelID)
    self.assertEqual(len(batch.objects), 1)

    result = batch.objects[0]
    self.assertIsInstance(result, ModelCommandResult)
    self.assertEqual(result.method, "defineModel")
    self.assertEqual(result.status, htmengineerrno.SUCCESS)
    self.assertEqual(result.commandID, "defineModelCmd1")

    # The second result batch should for the cloneModel result
    batch = resultBatches[1]
    self.assertEqual(batch.modelID, modelID)
    self.assertEqual(len(batch.objects), 1)

    result = batch.objects[0]
    self.assertIsInstance(result, ModelCommandResult)
    self.assertEqual(result.method, "cloneModel")
    self.assertEqual(result.status, htmengineerrno.SUCCESS)
    self.assertEqual(result.commandID, "cloneModelCmd1")

    # The third batch should be for the two input rows
    batch = resultBatches[2]
    self.assertEqual(batch.modelID, destModelID)
    self.assertEqual(len(batch.objects), len(inputRows))

    for inputRow, result in zip(inputRows, batch.objects):
      self.assertIsInstance(result, ModelInferenceResult)
      self.assertEqual(result.status, htmengineerrno.SUCCESS)
      self.assertEqual(result.rowID, inputRow.rowID)
      self.assertIsInstance(result.anomalyScore, float)

    # The fourth batch should be for the "deleteModel"
    batch = resultBatches[3]
    self.assertEqual(batch.modelID, destModelID)
    self.assertEqual(len(batch.objects), 1)

    result = batch.objects[0]
    self.assertIsInstance(result, ModelCommandResult)
    self.assertEqual(result.method, "deleteModel")
    self.assertEqual(result.status, htmengineerrno.SUCCESS)
    self.assertEqual(result.commandID, "deleteModelCmd1")

    # Signal Model Scheduler Service subprocess to shut down and wait for it
    waitResult = dict()
    def runWaiterThread():
      try:
        waitResult["returnCode"] = modelSchedulerSubprocess.wait()
      except:
        _LOGGER.exception("Waiting for modelSchedulerSubprocess failed")
        waitResult["exceptionInfo"] = traceback.format_exc()
        raise
      return

    modelSchedulerSubprocess.terminate()
    waiterThread = threading.Thread(target=runWaiterThread)
    waiterThread.setDaemon(True)
    waiterThread.start()
    waiterThread.join(timeout=30)
    self.assertFalse(waiterThread.isAlive())

    self.assertEqual(waitResult["returnCode"], 0, msg=repr(waitResult))
Beispiel #10
0
    def testRunModelWithFullThenIncrementalCheckpoints(self):
        # Have model_runner create a full checkpoint, then incremental checkpoint
        modelID = "foobar"

        checkpointMgr = model_checkpoint_mgr.ModelCheckpointMgr()

        with ModelSwapperInterface() as swapperAPI:
            possibleModels = getScalarMetricWithTimeOfDayParams(metricData=[0],
                                                                minVal=0,
                                                                maxVal=1000)

            # Submit requests including a model creation command and two data rows.
            args = possibleModels[0]
            args["inputRecordSchema"] = (
                FieldMetaInfo("c0", FieldMetaType.datetime,
                              FieldMetaSpecial.timestamp),
                FieldMetaInfo("c1", FieldMetaType.float,
                              FieldMetaSpecial.none),
            )

            # Define the model
            _LOGGER.info("Defining the model")
            swapperAPI.defineModel(modelID=modelID,
                                   args=args,
                                   commandID="defineModelCmd1")

            # Send input rows to the model
            inputRows = [
                ModelInputRow(
                    rowID="rowfoo",
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 00), 5.3]),
                ModelInputRow(
                    rowID="rowbar",
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 15), 2.4]),
            ]

            _LOGGER.info(
                "Submitting batch of %d input rows with ids=[%s..%s]...",
                len(inputRows), inputRows[0].rowID, inputRows[-1].rowID)
            swapperAPI.submitRequests(modelID=modelID, requests=inputRows)

            # Run model_runner and collect results
            with self._startModelRunnerSubprocess(
                    modelID) as modelRunnerProcess:
                resultBatches = self._consumeResults(numExpectedBatches=2,
                                                     timeout=15)
                self._waitForProcessToStopAndCheck(modelRunnerProcess)

            with MessageBusConnector() as bus:
                # The results message queue should be empty now
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

            self.assertEqual(len(resultBatches), 2, repr(resultBatches))

            # First result batch should be the first defineModel result
            batch = resultBatches[0]
            self.assertEqual(batch.modelID, modelID)
            self.assertEqual(len(batch.objects), 1)

            result = batch.objects[0]
            self.assertIsInstance(result, ModelCommandResult)
            self.assertEqual(result.method, "defineModel")
            self.assertEqual(result.status, htmengineerrno.SUCCESS)
            self.assertEqual(result.commandID, "defineModelCmd1")

            # The second result batch should be for the two input rows
            batch = resultBatches[1]
            self.assertEqual(batch.modelID, modelID)
            self.assertEqual(len(batch.objects), len(inputRows))

            for inputRow, result in zip(inputRows, batch.objects):
                self.assertIsInstance(result, ModelInferenceResult)
                self.assertEqual(result.status, htmengineerrno.SUCCESS)
                self.assertEqual(result.rowID, inputRow.rowID)
                self.assertIsInstance(result.anomalyScore, float)

            # Verify model checkpoint
            model = checkpointMgr.load(modelID)
            del model

            attrs = checkpointMgr.loadCheckpointAttributes(modelID)
            self.assertIn(
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                attrs,
                msg=repr(attrs))
            self.assertEqual(len(attrs[
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME]),
                             2,
                             msg=repr(attrs))
            self.assertNotIn(model_runner._ModelArchiver.
                             _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
                             attrs,
                             msg=repr(attrs))

            # Now, check incremental checkpointing
            inputRows2 = [
                ModelInputRow(
                    rowID=2,
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 20), 2.7]),
                ModelInputRow(
                    rowID=3,
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 25), 3.9]),
            ]

            _LOGGER.info(
                "Submitting batch of %d input rows with ids=[%s..%s]...",
                len(inputRows2), inputRows2[0].rowID, inputRows2[-1].rowID)
            inputBatchID = swapperAPI.submitRequests(modelID=modelID,
                                                     requests=inputRows2)

            with self._startModelRunnerSubprocess(
                    modelID) as modelRunnerProcess:
                resultBatches = self._consumeResults(numExpectedBatches=1,
                                                     timeout=15)
                self._waitForProcessToStopAndCheck(modelRunnerProcess)

            with MessageBusConnector() as bus:
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

            batch = resultBatches[0]
            self.assertEqual(batch.modelID, modelID)
            self.assertEqual(len(batch.objects), len(inputRows2))

            for inputRow, result in zip(inputRows2, batch.objects):
                self.assertIsInstance(result, ModelInferenceResult)
                self.assertEqual(result.status, htmengineerrno.SUCCESS)
                self.assertEqual(result.rowID, inputRow.rowID)
                self.assertIsInstance(result.anomalyScore, float)

            model = checkpointMgr.load(modelID)
            del model

            attrs = checkpointMgr.loadCheckpointAttributes(modelID)
            self.assertIn(
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                attrs,
                msg=repr(attrs))
            self.assertSequenceEqual(attrs[
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME],
                                     [inputBatchID],
                                     msg=repr(attrs))

            self.assertIn(model_runner._ModelArchiver.
                          _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
                          attrs,
                          msg=repr(attrs))

            self.assertSequenceEqual(
                model_runner._ModelArchiver._decodeDataSamples(
                    attrs[model_runner._ModelArchiver.
                          _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]),
                [row.data for row in inputRows2],
                msg=repr(attrs))

            # Final run with incremental checkpointing
            inputRows3 = [
                ModelInputRow(
                    rowID=4,
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 30), 4.7]),
                ModelInputRow(
                    rowID=5,
                    data=[datetime.datetime(2014, 5, 23, 8, 13, 35), 5.9]),
            ]

            _LOGGER.info(
                "Submitting batch of %d input rows with ids=[%s..%s]...",
                len(inputRows3), inputRows3[0].rowID, inputRows3[-1].rowID)
            inputBatchID = swapperAPI.submitRequests(modelID=modelID,
                                                     requests=inputRows3)

            with self._startModelRunnerSubprocess(
                    modelID) as modelRunnerProcess:
                resultBatches = self._consumeResults(numExpectedBatches=1,
                                                     timeout=15)
                self._waitForProcessToStopAndCheck(modelRunnerProcess)

            with MessageBusConnector() as bus:
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

            batch = resultBatches[0]
            self.assertEqual(batch.modelID, modelID)
            self.assertEqual(len(batch.objects), len(inputRows3))

            for inputRow, result in zip(inputRows3, batch.objects):
                self.assertIsInstance(result, ModelInferenceResult)
                self.assertEqual(result.status, htmengineerrno.SUCCESS)
                self.assertEqual(result.rowID, inputRow.rowID)
                self.assertIsInstance(result.anomalyScore, float)

            model = checkpointMgr.load(modelID)
            del model

            attrs = checkpointMgr.loadCheckpointAttributes(modelID)

            self.assertIn(
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME,
                attrs,
                msg=repr(attrs))
            self.assertSequenceEqual(attrs[
                model_runner._ModelArchiver._BATCH_IDS_CHECKPOINT_ATTR_NAME],
                                     [inputBatchID],
                                     msg=repr(attrs))

            self.assertIn(model_runner._ModelArchiver.
                          _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME,
                          attrs,
                          msg=repr(attrs))
            self.assertSequenceEqual(
                model_runner._ModelArchiver._decodeDataSamples(
                    attrs[model_runner._ModelArchiver.
                          _INPUT_SAMPLES_SINCE_CHECKPOINT_ATTR_NAME]),
                [row.data for row in itertools.chain(inputRows2, inputRows3)],
                msg=repr(attrs))

            # Delete the model
            _LOGGER.info("Deleting the model=%s", modelID)
            swapperAPI.deleteModel(modelID=modelID,
                                   commandID="deleteModelCmd1")

            with self._startModelRunnerSubprocess(
                    modelID) as modelRunnerProcess:
                resultBatches = self._consumeResults(numExpectedBatches=1,
                                                     timeout=15)
                self._waitForProcessToStopAndCheck(modelRunnerProcess)

            self.assertEqual(len(resultBatches), 1, repr(resultBatches))

            # First result batch should be the first defineModel result
            batch = resultBatches[0]
            self.assertEqual(batch.modelID, modelID)
            self.assertEqual(len(batch.objects), 1)

            result = batch.objects[0]
            self.assertIsInstance(result, ModelCommandResult)
            self.assertEqual(result.method, "deleteModel")
            self.assertEqual(result.status, htmengineerrno.SUCCESS)
            self.assertEqual(result.commandID, "deleteModelCmd1")

            with MessageBusConnector() as bus:
                self.assertTrue(bus.isEmpty(swapperAPI._resultsQueueName))

                # The model input queue should be deleted now
                self.assertFalse(
                    bus.isMessageQeueuePresent(
                        swapperAPI._getModelInputQName(modelID=modelID)))

            # The model checkpoint should be gone too
            with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
                checkpointMgr.load(modelID)

            with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
                checkpointMgr.loadModelDefinition(modelID)

            with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
                checkpointMgr.loadCheckpointAttributes(modelID)

            with self.assertRaises(model_checkpoint_mgr.ModelNotFound):
                checkpointMgr.remove(modelID)