def test_use_different_unit_in_performances(self): problem1 = self.create_simple_problem( time_unit_price="h", time_unit_performance="h", timeslot="h", base_price=100, base_perf=1000, base_workload=2000, n_timeslots=365 * 24, ) phaseI1 = phases.PhaseI(problem1) solution1 = phaseI1.solve() assert solution1.solving_stats.algorithm.status == Status.optimal # Now the same problem, but the performances are expressed in qps instead of rph problem2 = self.create_simple_problem( time_unit_price="h", time_unit_performance="s", timeslot="h", base_price=100, base_perf=1000 / 3600, base_workload=2000, n_timeslots=365 * 24, ) phaseI2 = phases.PhaseI(problem2) solution2 = phaseI2.solve() # The solution should be the same, because the performance is the same (only scaled) assert solution2.solving_stats.algorithm.status == Status.optimal assert ( solution2.solving_stats.optimal_cost == solution1.solving_stats.optimal_cost ) assert solution2.allocation.values == solution1.allocation.values
def test_different_units_in_workload(self): problem1 = self.create_simple_problem( time_unit_price="h", time_unit_performance="h", timeslot="h", base_price=100, base_perf=1000, base_workload=2000, n_timeslots=365 * 24, ) phaseI1 = phases.PhaseI(problem1) solution1 = phaseI1.solve() assert solution1.solving_stats.algorithm.status == Status.optimal # Now solve the same problem, but with the workload in minutes problem2 = self.create_simple_problem( time_unit_price="h", time_unit_performance="h", timeslot="m", base_price=100, base_perf=1000, base_workload=2000 / 60, n_timeslots=365 * 24 * 60, ) phaseI2 = phases.PhaseI(problem2) solution2 = phaseI2.solve() # The solution should be the same, because the performance is the same assert solution2.solving_stats.algorithm.status == Status.optimal assert ( solution2.solving_stats.optimal_cost == solution1.solving_stats.optimal_cost ) assert solution1.reserved_allocation == solution2.reserved_allocation
def test_solution_with_hybrid_to_yaml_back_and_forth(self): """Creates and solves a problem which uses private instances, converts the solution to YAML, checks that the resulting YAML is valid, and finally reads it back to Python and compares it with the solution object""" problem = create_problem() solution = phases.PhaseI(problem).solve() # Dump solution to yaml from malloovia import util yaml_str = util.solutions_to_yaml([solution]) with open("/tmp/test.yaml", "w") as f: f.write(yaml_str) # Check that the generated solution is valid against the schema solution_data = yaml.safe_load(yaml_str) malloovia_schema = util.get_schema() try: validate(solution_data, malloovia_schema) except Exception as e: pytest.fail("The generated yaml is not valid against the schema") # The actual test is to read it back back_to_solution = util.solutions_from_dict(yaml.safe_load(yaml_str), yaml_filename="Nofile") # Compare malloovia classes to ensure that they store the same information in the # original problem. assert solution == back_to_solution['solution_i_example']
def test_solve_problem_with_private_constraints(self): """This test creates and solves one problem in which the private cloud imposes limits on the maximum number of cores""" problem = create_problem() phase_i = phases.PhaseI(problem) solution = phase_i.solve() assert solution.solving_stats.algorithm.status == Status.optimal # The cost of the optimal solution has to be 252.0 for this problem assert solution.solving_stats.optimal_cost == 252.0 # There is a single private limiting set private_limiting_sets = set(ls for ic in problem.instance_classes if ic.is_private for ls in ic.limiting_sets) assert len(private_limiting_sets) == 1 private_limiting_set = list(private_limiting_sets)[0] # That limiting set imposes a limit in the number of cores max_cores = private_limiting_set.max_cores assert max_cores == 20 # The solution does not violate the max_cores limit used_cores = [] for ts_alloc in solution.allocation.values: used_cores_in_ts = 0 for app_alloc in ts_alloc: for i, ic_number in enumerate(app_alloc): ic = solution.allocation.instance_classes[i] if ic.is_private: used_cores_in_ts += ic_number * ic.cores used_cores.append(used_cores_in_ts) assert all(n_cores <= max_cores for n_cores in used_cores)
def test_use_different_unit_in_prices(self): problem1 = self.create_simple_problem( time_unit_price="h", time_unit_performance="h", timeslot="h", base_price=100, base_perf=1000, base_workload=2000, n_timeslots=365 * 24, ) phaseI1 = phases.PhaseI(problem1) solution1 = phaseI1.solve() assert solution1.solving_stats.algorithm.status == Status.optimal # Now solve the same problem, but with the prices in minutes, not scaled problem2 = self.create_simple_problem( time_unit_price="m", time_unit_performance="h", timeslot="h", base_price=100, base_perf=1000, base_workload=2000, n_timeslots=365 * 24, ) phaseI2 = phases.PhaseI(problem2) solution2 = phaseI2.solve() # Since 100 is now the price per minute instead of per hour, the total cost should # be 60 times the one of the first problem assert solution2.solving_stats.algorithm.status == Status.optimal assert ( solution2.solving_stats.optimal_cost == 60 * solution1.solving_stats.optimal_cost ) # But, except for the cost, the solution is equivalent assert solution2.allocation.values == solution1.allocation.values
def test_use_different_unit_in_phaseII_workload(self): # In phase I, the prices and performances are expressed per hour # The workload is also per hour. To reduce the size of the problem, # we use 24h as reservation period instead of 8760 phase_i_problem = self.create_simple_problem( time_unit_price="h", time_unit_performance="h", timeslot="h", base_price=100, base_perf=1000, base_workload=2000, n_timeslots=24, ) phaseI = phases.PhaseI(phase_i_problem) solution_i = phaseI.solve() assert solution_i.solving_stats.algorithm.status == Status.optimal # For phase II, we keep the same instance classes (prices and performances) # but use a STWP expressed in minutes app0 = phase_i_problem.workloads[0].app phase_ii_problem = phase_i_problem._replace( workloads=( Workload( "wl_app0", description="Test", app=app0, values=(2000 / 3600,) * 24 * 60, time_unit="m", ), ) ) phase_ii = phases.PhaseII(problem=phase_ii_problem, phase_i_solution=solution_i) solution_ii = phase_ii.solve_period() # Since the workload was scaled, it is essentially the same, so the phase II solution # should be equal to phase I solution, except for rounding errors assert ( abs( solution_ii.global_solving_stats.optimal_cost - solution_i.solving_stats.optimal_cost ) <= 0.0001 )