コード例 #1
0
    def test_four_corners_save_state_load_state(self):
        four_corners_directory = os.path.join(environment.top, "curie", "yaml",
                                              "four_corners_microbenchmark")
        scenario = scenario_parser.from_path(four_corners_directory)
        scenario.cluster = mock_cluster()
        scenario.output_directory = environment.test_output_dir(self)

        scenario.save_state()
        unfrozen_caveman_scenario = scenario.load_state(
            environment.test_output_dir(self))

        self.assertEqual(scenario.id, unfrozen_caveman_scenario.id)
        self.assertEqual(None, unfrozen_caveman_scenario.cluster)
        self.assertEqual(6, len(unfrozen_caveman_scenario.results_map))
コード例 #2
0
ファイル: test_meta.py プロジェクト: nutanix/curie
 def setUp(self):
     self.cluster = mock_cluster()
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self))
     self.vm_group = VMGroup(self.scenario, "group_0")
     self.scenario.vm_groups = {self.vm_group._name: self.vm_group}
コード例 #3
0
    def setUp(self):
        self.cluster = mock.Mock(spec=Cluster)
        self.cluster.name.return_value = "Mock Cluster"
        self.vms = [mock.Mock(spec=Vm) for _ in xrange(4)]
        self.cluster.find_vms_regex.return_value = self.vms
        _nodes = [mock.Mock(spec=Node) for _ in xrange(4)]

        self.cluster_metadata = CurieSettings.Cluster()
        self.cluster_metadata.cluster_name = "mock_cluster"
        self.cluster_metadata.cluster_hypervisor_info.CopyFrom(
            CurieSettings.Cluster.ClusterHypervisorInfo())
        self.cluster_metadata.cluster_management_server_info.CopyFrom(
            CurieSettings.Cluster.ClusterManagementServerInfo())
        self.cluster_metadata.cluster_software_info.CopyFrom(
            CurieSettings.Cluster.ClusterSoftwareInfo())
        for id, node in enumerate(_nodes):
            node.node_id.return_value = id
            curr_node = self.cluster_metadata.cluster_nodes.add()
            curr_node.id = str(id)
        self.cluster.metadata.return_value = self.cluster_metadata

        self.cluster.nodes.return_value = _nodes
        self.cluster.node_count.return_value = len(_nodes)
        self.scenario = Scenario(
            cluster=self.cluster,
            output_directory=environment.test_output_dir(self),
            goldimages_directory="/fake/goldimages/directory/")
        self.vm_group = VMGroup(self.scenario, "group_0.*[(-!&")
        for mock_vm, vm_name in zip(self.vms, self.vm_group.get_vms_names()):
            mock_vm.vm_name.return_value = vm_name
        self.scenario.vm_groups = {self.vm_group._name: self.vm_group}
コード例 #4
0
  def test_PrismHostsInSameCluster_missing(self, m_NutanixRestApiClient):
    self.cluster = mock_cluster(spec=NutanixVsphereCluster)
    self.scenario = Scenario(
      cluster=self.cluster,
      output_directory=environment.test_output_dir(self))

    m_client = mock.Mock()
    m_client.host = "fake_prism_host"
    m_client.hosts_get.return_value = {
      "entities": [
        {"hypervisorAddress": "169.254.0.0"},
        {"hypervisorAddress": "169.254.0.1"},
        # {"hypervisorAddress": "169.254.0.2"},
        {"hypervisorAddress": "169.254.0.3"},
      ]
    }
    m_NutanixRestApiClient.from_proto.return_value = m_client

    step = check.PrismHostInSameCluster(self.scenario, 2)
    with self.assertRaises(CurieTestException) as ar:
      step()
    self.assertEqual(
      "Cause: Node '2' with hypervisor address '169.254.0.2' is not a member "
      "of the Nutanix cluster managed at 'fake_prism_host'.\n\n"
      "Impact: The configured nodes belong to multiple Nutanix clusters, "
      "which is not supported.\n\n"
      "Corrective Action: Please choose a set of nodes that belong to a "
      "single Nutanix cluster. If the target is configured for metro "
      "availability, please choose nodes that all belong to a single site.\n\n"
      "Traceback: None",
      str(ar.exception))
コード例 #5
0
ファイル: test_check.py プロジェクト: nutanix/curie
 def setUp(self):
   self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
   self.cluster.update_metadata(False)
   self.scenario = Scenario(
     cluster=self.cluster,
     output_directory=environment.test_output_dir(self),
     goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)
コード例 #6
0
 def test_append_cluster_stats_duplicates(self):
     output_dir = os.path.join(environment.test_output_dir(self),
                               "test_append_cluster_stats_duplicates")
     if os.path.isdir(output_dir):
         shutil.rmtree(output_dir)
     metric_to_append = CurieMetric()
     metric_to_append.CopyFrom(self.counter_template)
     metric_to_append.timestamps.extend(
         [1454092320, 1454092321, 1454092322])
     metric_to_append.values.extend([1, 2, 3])
     new_results = {"node_0": [metric_to_append]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     metric_to_append = CurieMetric()
     metric_to_append.CopyFrom(self.counter_template)
     metric_to_append.timestamps.extend([1454092322, 1454092323])
     metric_to_append.values.extend([3, 4])
     new_results = {"node_0": [metric_to_append]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     expected_metric = CurieMetric()
     expected_metric.CopyFrom(self.counter_template)
     expected_metric.timestamps.extend(
         [1454092320, 1454092321, 1454092322, 1454092323])
     expected_metric.values.extend([1, 2, 3, 4])
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], expected_metric)
コード例 #7
0
 def setUp(self):
     self.cluster = mock.Mock(spec=Cluster)
     self.vms = [mock.Mock(spec=Vm) for _ in xrange(4)]
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self))
     self.iogen = iogen.IOGen("test_iogen", self.scenario, "", "test_iogen")
コード例 #8
0
ファイル: test_nodes.py プロジェクト: nutanix/curie
  def setUp(self):
    self.cluster = mock.Mock(spec=Cluster)

    proto_patch_encryption_support(CurieSettings)
    cluster_metadata = CurieSettings.Cluster()
    cluster_metadata.cluster_name = "MockCluster"
    self.nodes = [mock.Mock(spec=Node) for _ in xrange(4)]
    for id, node in enumerate(self.nodes):
      node.node_id.return_value = id
      node.node_index.return_value = id
      curr_node = cluster_metadata.cluster_nodes.add()
      curr_node.id = str(id)
      oob_info = curr_node.node_out_of_band_management_info
      oob_info.interface_type = oob_info.kIpmi
      oob_info.vendor = oob_info.kUnknownVendor
      oob_info.ip_address = "1.2.3.%d" % id
      oob_info.username = "******"
      oob_info.password = "******"

    self.cluster.nodes.return_value = self.nodes
    self.cluster.node_count.return_value = len(self.nodes)
    self.cluster.metadata.return_value = cluster_metadata

    self.scenario = Scenario(
      cluster=self.cluster,
      output_directory=environment.test_output_dir(self))
コード例 #9
0
 def test_append_cluster_stats_corrupt(self):
     output_dir = os.path.join(environment.test_output_dir(self),
                               "test_append_cluster_stats_corrupt")
     if os.path.isdir(output_dir):
         shutil.rmtree(output_dir)
     metric_to_append = CurieMetric()
     metric_to_append.CopyFrom(self.counter_template)
     metric_to_append.timestamps.extend([1454092320, 1454092321])
     metric_to_append.values.extend([1, 2])
     new_results = {"node_0": [metric_to_append]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     # Corrupt the file.
     filename = ("%s_%s" % (self._counter_template_name(),
                            self.counter_template.instance)).replace(
                                ".", "_")
     bin_path = os.path.join(output_dir, "node_0", filename + ".bin")
     assert (os.path.isfile(bin_path))
     with open(bin_path, "w") as f:
         f.write("Cela ne veut pas un protobuf.")
     metric_to_append = CurieMetric()
     metric_to_append.CopyFrom(self.counter_template)
     metric_to_append.timestamps.extend([1454092322, 1454092323])
     metric_to_append.values.extend([3, 4])
     new_results = {"node_0": [metric_to_append]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     expected_metric = CurieMetric()
     expected_metric.CopyFrom(self.counter_template)
     expected_metric.timestamps.extend([1454092322, 1454092323])
     expected_metric.values.extend([3, 4])
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], expected_metric)
コード例 #10
0
ファイル: test_vm_group_result.py プロジェクト: nutanix/curie
 def setUp(self):
     self.vm_group = mock.Mock(spec=VMGroup)
     self.vm_group.get_vms.return_value = []
     self.cluster = mock.Mock(spec=Cluster)
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self))
コード例 #11
0
  def setUp(self):
    # Create a scenario
    self.scenario = Mock(spec=Scenario)
    self.scenario.display_name = "Mock Scenario"
    self.scenario.output_directory = environment.test_output_dir(self)
    self.scenario.vm_groups = {}
    self.scenario.phase = Phase(Phase.RUN)

    # Create a cluster
    self.cluster_size = 4
    self.scenario.cluster = Mock(spec=Cluster)
    self.scenario.cluster.nodes.return_value = []
    self.scenario.cluster.vms.return_value = []

    # Create nodes that get returned from APIs
    nodes = [Mock(spec=Node) for _ in xrange(self.cluster_size)]
    ipaddr = TestStepsPlaybook.__ipaddr(1)
    for id, node in enumerate(nodes):
      node.node_id.return_value = id
      node.node_ip.return_value = next(ipaddr)
    self.scenario.cluster.nodes.return_value = nodes

    # Create CVM VMs
    cvms = self.__create_mock_vms(self.cluster_size, is_cvm=True, ip_index=5)
    self.scenario.cluster.vms.return_value.extend(cvms)

    # Create nodes that are created from metadata/YAML
    cluster_nodes = [MagicMock(id=uuid.uuid4())
                     for _ in range(self.cluster_size)]
    ipaddr = TestStepsPlaybook.__ipaddr(self.cluster_size + 1)
    for cluster_node in cluster_nodes:
      cluster_node.svm_addr = next(ipaddr)
    self.scenario.cluster.metadata.return_value.cluster_nodes = cluster_nodes
コード例 #12
0
    def test_four_corners_save_state_load_state_experimental_metrics(self):
        four_corners_directory = os.path.join(environment.top, "curie", "yaml",
                                              "four_corners_microbenchmark")
        scenario = scenario_parser.from_path(four_corners_directory,
                                             enable_experimental_metrics=True)
        scenario.cluster = mock_cluster()
        scenario.output_directory = environment.test_output_dir(self)

        scenario.save_state()
        unfrozen_caveman_scenario = scenario.load_state(
            environment.test_output_dir(self))

        self.assertEqual(scenario.id, unfrozen_caveman_scenario.id)
        self.assertEqual(None, unfrozen_caveman_scenario.cluster)
        self.assertEqual(7, len(unfrozen_caveman_scenario.results_map))
        for result in unfrozen_caveman_scenario.results_map.itervalues():
            if isinstance(result, ClusterResult):
                self.assertIsInstance(result.metric, CurieMetric)
コード例 #13
0
ファイル: test_vsphere_cluster.py プロジェクト: nutanix/curie
 def setUp(self):
   self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
   self.cluster.update_metadata(False)
   assert isinstance(self.cluster, VsphereCluster), \
          "This test must run on a vCenter cluster"
   self.scenario = Scenario(
     cluster=self.cluster,
     output_directory=environment.test_output_dir(self),
     goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)
コード例 #14
0
ファイル: test_cluster.py プロジェクト: nutanix/curie
 def setUp(self):
     self.cluster = mock_cluster()
     vms = [mock.Mock(spec=Vm) for _ in xrange(4)]
     for index, vm in enumerate(vms):
         vm.vm_name.return_value = "fake_vm_%d" % index
     self.cluster.vms.return_value = vms
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self))
コード例 #15
0
 def test_runtime_variables_empty(self):
   ahv_cluster = Mock(spec=AcropolisCluster)
   self.scenario.cluster = ahv_cluster
   vars_in = {}
   vars_out_expected = {"hypervisor": "ahv",
                        "output_directory": environment.test_output_dir(self)}
   vars_out_actual = steps.playbook.Run._runtime_variables(vars_in,
                                                           self.scenario)
   self.assertDictEqual(vars_out_expected, vars_out_actual)
コード例 #16
0
 def test_save_state_unexpected_oserror(self, mock_makedirs):
     scenario = Scenario()
     scenario.cluster = mock_cluster()
     scenario.output_directory = environment.test_output_dir(self)
     err = OSError()
     err.errno = errno.EIO
     mock_makedirs.side_effect = err
     with self.assertRaises(OSError) as ar:
         scenario.save_state()
     self.assertEqual(errno.EIO, ar.exception.errno)
コード例 #17
0
 def test_from_path_none_found(self):
   # Create a directory that contains no YAML configurations.
   path = environment.test_output_dir(self)
   if os.path.isdir(path):
     os.rmdir(path)
   os.makedirs(path)
   with self.assertRaises(ValueError) as ar:
     scenario_parser.from_path(path)
   self.assertEqual("No configuration files found at '%s'" % path,
                    str(ar.exception))
コード例 #18
0
 def test_VSphereDatastoreVisible_nodeid_is_hostname(self):
   self.cluster = mock_cluster(spec=VsphereCluster)
   self.scenario = Scenario(
     cluster=self.cluster,
     output_directory=environment.test_output_dir(self))
   for index, cluster_node in enumerate(self.cluster.nodes()):
     cluster_node.node_id.return_value = "fake_node_%d.rtp.nutanix.com" % index
   step = check.VSphereDatastoreVisible(self.scenario, "fake_datastore_name", 0)
   step()
   self.scenario.cluster.datastore_visible.assert_called_once_with("fake_datastore_name", host_name="fake_node_0.rtp.nutanix.com")
コード例 #19
0
ファイル: test_iogen_result.py プロジェクト: nutanix/curie
 def setUp(self):
   self.iogen = mock.Mock(spec=IOGen)
   self.iogen.reporting_interval = 1
   self.workload = mock.Mock(spec=Workload)
   self.workload.iogen.return_value = self.iogen
   vm_group = mock.Mock(spec=VMGroup)
   vm_group.get_vms.return_value = []
   self.cluster = mock.Mock(spec=Cluster)
   self.scenario = Scenario(
     cluster=self.cluster,
     output_directory=environment.test_output_dir(self))
コード例 #20
0
ファイル: test_vm_group.py プロジェクト: nutanix/curie
 def setUp(self):
   self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
   self.cluster.update_metadata(False)
   self.scenario = Scenario(
     cluster=self.cluster,
     output_directory=environment.test_output_dir(self),
     goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)
   self.uuid = str(uuid.uuid4())
   log.debug("Setting up test %s - tagged with UUID %s", self._testMethodName,
             self.uuid)
   self.group_name = self.uuid[0:VMGroup.MAX_NAME_LENGTH]
コード例 #21
0
 def setUp(self):
     self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
     self.cluster.update_metadata(False)
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self),
         goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)
     self.group_name = "".join([
         random.choice(string.printable)
         for _ in xrange(VMGroup.MAX_NAME_LENGTH)
     ])
コード例 #22
0
 def setUp(self):
     self.cluster_size = 4
     self.cluster = mock.Mock(spec=Cluster)
     self.nodes = [mock.Mock(spec=Node) for _ in xrange(self.cluster_size)]
     for id, node in enumerate(self.nodes):
         node.node_id.return_value = id
     self.cluster.nodes.return_value = self.nodes
     self.scenario = Scenario(
         cluster=self.cluster,
         output_directory=environment.test_output_dir(self))
     self.__delete_output_directory()
     os.makedirs(self.scenario.output_directory)
コード例 #23
0
  def test_four_corners(self):
    scenario_directory = os.path.join(
      environment.top, "curie", "yaml", "four_corners_microbenchmark")
    scenario = scenario_parser.from_path(
      scenario_directory,
      cluster=self.cluster,
      output_directory=environment.test_output_dir(self),
      goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)

    scenario.start()
    scenario.join()
    self.assertEqual(Status.SUCCEEDED, scenario.status(),
                     msg=scenario.error_message())
コード例 #24
0
  def test_vdi_low_count(self):
    scenario_directory = os.path.join(
      environment.top, "curie", "yaml", "vdi_simulator_task_100")
    scenario = scenario_parser.from_path(
      scenario_directory,
      cluster=self.cluster,
      output_directory=environment.test_output_dir(self),
      goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir,
      vars={"vms_per_node": 1, "runtime_secs": 60}
    )

    scenario.start()
    scenario.join()
    self.assertEqual(Status.SUCCEEDED, scenario.status(),
                     msg=scenario.error_message())
コード例 #25
0
ファイル: test_workload.py プロジェクト: nutanix/curie
 def setUp(self):
   self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
   self.cluster.update_metadata(False)
   self.group_name = "".join(
     [random.choice(string.printable)
      for _ in xrange(VMGroup.MAX_NAME_LENGTH)])
   self.workload_name = "".join(
     [random.choice(string.printable) for _ in xrange(40)])
   self.scenario = Scenario(
     cluster=self.cluster,
     source_directory=os.path.join(environment.resource_dir(), "fio"),
     output_directory=environment.test_output_dir(self),
     goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir)
   self.valid_fio_path = "oltp.fio"
   self.invalid_fio_path = "not-a-file.bogus"
コード例 #26
0
 def setUp(self):
     self.cluster = util.cluster_from_json(gflags.FLAGS.cluster_config_path)
     self.cluster.update_metadata(False)
     self.scenario = Scenario(
         cluster=self.cluster,
         goldimages_directory=gflags.FLAGS.curie_vmdk_goldimages_dir,
         output_directory=environment.test_output_dir(self),
         source_directory=os.path.join(environment.resource_dir(),
                                       "playbooks"))
     self.scenario.vm_groups = dict()
     self.uuid = str(uuid.uuid4())
     log.debug("Setting up test %s - tagged with UUID %s",
               self._testMethodName, self.uuid)
     self.vm_group_names = \
       [(self.uuid[0:(VMGroup.MAX_NAME_LENGTH - 2)] + "-" + str(i))
        for i in range(2)]
コード例 #27
0
ファイル: test_cluster.py プロジェクト: nutanix/curie
 def test_collect_dump_read_stats(self):
     results_map = self.cluster.collect_performance_stats()
     dir_name = environment.test_output_dir(self)
     ScenarioUtil.append_cluster_stats(results_map, dir_name)
     read_results_map = ScenarioUtil.read_cluster_stats(dir_name)
     self.assertEqual(results_map.keys(), read_results_map.keys())
     for node_id in results_map:
         self.assertEqual(len(results_map[node_id]),
                          len(read_results_map[node_id]))
         for expected_metric, metric in zip(results_map[node_id],
                                            read_results_map[node_id]):
             self.assertEqual(MetricsUtil.metric_name(metric),
                              MetricsUtil.metric_name(expected_metric))
             self.assertEqual(metric.instance, expected_metric.instance)
             self.assertEqual(metric.timestamps, expected_metric.timestamps)
             self.assertEqual(metric.values, expected_metric.values)
コード例 #28
0
ファイル: test_workload.py プロジェクト: nutanix/curie
 def setUp(self):
   self.cluster = mock.Mock(spec=Cluster)
   self.vms = [mock.Mock(spec=Vm) for _ in xrange(4)]
   self.nodes = []
   for index in range(4):
     node = mock.Mock(spec=Node)
     node.node_id.return_value = "node_%d" % index
     self.nodes.append(node)
   self.cluster.nodes.return_value = self.nodes
   self.cluster.node_count.return_value = len(self.cluster.nodes())
   self.scenario = Scenario(
     cluster=self.cluster,
     source_directory=os.path.join(environment.resource_dir(), "fio"),
     output_directory=environment.test_output_dir(self))
   self.vm_group = VMGroup(self.scenario, "group_0")
   for mock_vm, vm_name in zip(self.vms, self.vm_group.get_vms_names()):
     mock_vm.vm_name.return_value = vm_name
   self.scenario.vm_groups = {self.vm_group._name: self.vm_group}
コード例 #29
0
 def setUp(self):
     path = environment.test_output_dir(self)
     if os.path.isdir(path):
         shutil.rmtree(path)
     os.makedirs(path)
     self.scenario = Scenario(name="Fake Scenario")
     self.scenario.cluster = mock_cluster()
     self.scenario.output_directory = path
     self.mock_setup_step = mock.Mock(spec=BaseStep)
     self.mock_setup_step.description = "Some mock Setup thing"
     self.mock_setup_step.status = Status.NOT_STARTED
     self.mock_setup_step.requirements.return_value = set()
     self.mock_run_step = mock.Mock(spec=BaseStep)
     self.mock_run_step.description = "Some mock Run thing"
     self.mock_run_step.status = Status.NOT_STARTED
     self.mock_run_step.requirements.return_value = set()
     self.scenario.add_step(self.mock_setup_step, Phase.SETUP)
     self.scenario.add_step(self.mock_run_step, Phase.RUN)
     self.scenario._status = Status.EXECUTING
コード例 #30
0
 def test_append_read_cluster_stats_empty(self):
     output_dir = os.path.join(environment.test_output_dir(self),
                               "test_append_read_cluster_stats_empty")
     if os.path.isdir(output_dir):
         shutil.rmtree(output_dir)
     empty_metric = CurieMetric()
     empty_metric.CopyFrom(self.counter_template)
     del empty_metric.timestamps[:]
     del empty_metric.values[:]
     self.assertEqual(empty_metric.timestamps, [])
     self.assertEqual(empty_metric.values, [])
     # Write empty.
     new_results = {"node_0": [empty_metric]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], empty_metric)
     # Append empty.
     new_results = {"node_0": [empty_metric]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], empty_metric)
     # Append non-empty.
     non_empty_metric = CurieMetric()
     non_empty_metric.CopyFrom(self.counter_template)
     non_empty_metric.timestamps.extend([1454092320, 1454092321])
     non_empty_metric.values.extend([1, 2])
     new_results = {"node_0": [non_empty_metric]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], non_empty_metric)
     # Append empty again.
     new_results = {"node_0": [empty_metric]}
     ScenarioUtil.append_cluster_stats(new_results, output_dir)
     results = ScenarioUtil.read_cluster_stats(output_dir)
     self.assertEqual(results.keys(), ["node_0"])
     self.assertEqual(len(results["node_0"]), 1)
     self.assertEqual(results["node_0"][0], non_empty_metric)