def test_balance_status_in_progress(self): """ Moc return value of run_command executing btrfs balance status pool_mount_point which is invoked inside of target function. :return: """ # balance_status called with pool object of name=Pool object # # typical return for no current balance operation in progress: # out=["No balance found on '/mnt2/single-to-raid1'", ''] # err=[''] # rc=0 # example return for ongoing balance operation: pool = Pool(raid='raid0', name='test-pool') out = [ "Balance on '/mnt2/rock-pool' is running", '7 out of about 114 chunks balanced (8 considered), 94% left', '' ] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'running', 'percent_done': 6} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg=("Failed to correctly identify " "balance running status"))
def test_balance_status_in_progress(self): """ Moc return value of run_command executing btrfs balance status pool_mount_point which is invoked inside of target function. :return: """ # balance_status called with pool object of name=Pool object # # typical return for no current balance operation in progress: # out=["No balance found on '/mnt2/single-to-raid1'", ''] # err=[''] # rc=0 # example return for ongoing balance operation: pool = Pool(raid='raid0', name='test-pool') out = ["Balance on '/mnt2/rock-pool' is running", '7 out of about 114 chunks balanced (8 considered), 94% left', ''] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'running', 'percent_done': 6} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg="Failed to correctly identify balance running status")
def _balance_status(pool): try: # acquire a handle on the last pool balance status db entry ps = PoolBalance.objects.filter(pool=pool).order_by('-id')[0] except: # return empty handed if we have no 'last entry' to update return Response() # Check if we have a running task which matches our last pool status tid if (Task.objects.filter(uuid=ps.tid).exists()): to = Task.objects.get(uuid=ps.tid) if (to.failed is not None): ps.status = 'failed' ps.message = to.last_exception ps.end_time = to.failed ps.save() to.delete() return ps # Get the current status of balance on this pool, irrespective of # a running balance task, ie command line intervention. cur_status = balance_status(pool) previous_status = ps.status # TODO: future "Balance Cancel" button should call us to have these # TODO: values updated in the db table ready for display later. if previous_status == 'cancelling' \ and cur_status['status'] == 'finished': # override current status as 'cancelled' cur_status['status'] = 'cancelled' cur_status['message'] = \ 'cancelled at %s%% complete' % ps.percent_done # and retain prior percent finished value cur_status['percent_done'] = ps.percent_done if previous_status != 'finished' and previous_status != 'cancelled': # update the last pool balance status with current status info. PoolBalance.objects.filter(id=ps.id).update(**cur_status) return ps
def _balance_status(self, pool, disk): try: ps = PoolBalance.objects.filter(pool=pool).order_by('-id')[0] except: return Response() if (ps.status == 'started' or ps.status == 'running'): cur_status = balance_status(pool, disk.name) PoolBalance.objects.filter(id=ps.id).update(**cur_status) return ps
def _balance_status(pool, disk): try: ps = PoolBalance.objects.filter(pool=pool).order_by('-id')[0] except: return Response() if (ps.status == 'started' or ps.status == 'running'): cur_status = balance_status(pool, disk.name) PoolBalance.objects.filter(id=ps.id).update(**cur_status) return ps
def test_balance_status_paused(self): """ Test to see if balance_status() correctly identifies a Paused balance state. :return: """ pool = Pool(raid='raid0', name='test-pool') out = ["Balance on '/mnt2/rock-pool' is paused", '3 out of about 114 chunks balanced (4 considered), 97% left', ''] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'paused', 'percent_done': 3} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg="Failed to correctly identify balance paused status")
def test_balance_status_cancel_requested(self): """ As per test_balance_status_in_progress(self) but while balance is :return: """ pool = Pool(raid='raid0', name='test-pool') # run_command moc return values. out = ["Balance on '/mnt2/rock-pool' is running, cancel requested", '15 out of about 114 chunks balanced (16 considered), 87% left', ''] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'cancelling', 'percent_done': 13} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg="Failed to correctly identify balance cancel requested status")
def _balance_status(pool): try: ps = PoolBalance.objects.filter(pool=pool).order_by('-id')[0] except: return Response() if (Task.objects.filter(uuid=ps.tid).exists()): to = Task.objects.get(uuid=ps.tid) if (to.failed is not None): ps.status = 'failed' ps.message = to.last_exception ps.end_time = to.failed ps.save() to.delete() return ps elif (ps.status == 'started' or ps.status == 'running'): #task finished sucessfully or is still running cur_status = balance_status(pool) PoolBalance.objects.filter(id=ps.id).update(**cur_status) return ps
def test_balance_status_paused(self): """Test to see if balance_status() correctly identifies a Paused balance state. :return: """ pool = Pool(raid='raid0', name='test-pool') out = [ "Balance on '/mnt2/rock-pool' is paused", '3 out of about 114 chunks balanced (4 considered), 97% left', '' ] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'paused', 'percent_done': 3} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg=("Failed to correctly identify balance " "paused status"))
def test_balance_status_pause_requested(self): """ As per test_balance_status_in_progress(self) but while pause requested :return: """ pool = Pool(raid='raid0', name='test-pool') out = [ "Balance on '/mnt2/rock-pool' is running, pause requested", '3 out of about 114 chunks balanced (4 considered), 97% left', '' ] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'pausing', 'percent_done': 3} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual( balance_status(pool), expected_results, msg="Failed to correctly identify balance pause requested status")
def test_balance_status_cancel_requested(self): """ As per test_balance_status_in_progress(self) but while balance is :return: """ pool = Pool(raid='raid0', name='test-pool') # run_command moc return values. out = [ "Balance on '/mnt2/rock-pool' is running, cancel requested", ('15 out of about 114 chunks balanced (16 considered), ' '87% left'), '' ] err = [''] # N.B. the return code for in progress balance = 1 rc = 1 expected_results = {'status': 'cancelling', 'percent_done': 13} self.mock_run_command.return_value = (out, err, rc) self.mock_mount_root.return_value = '/mnt2/test-mount' self.assertEqual(balance_status(pool), expected_results, msg=("Failed to correctly identify balance cancel " "requested status"))
def _balance_status(pool): try: # acquire a handle on the last pool balance status db entry ps = PoolBalance.objects.filter(pool=pool).order_by("-id")[0] except: # return empty handed if we have no 'last entry' to update return Response() # Check if we have a pending task which matches our tid. logger.debug("POOLS BALANCE MODEL PS.TID = {}".format(ps.tid)) hi = HUEY logger.debug("HUEY.pending() {}".format(hi.pending())) # Pending balance tasks. N.B. Executing tasks are no longer pending. # There is a 1 to 3 second 'pending" status for Huey tasks. pending_task_ids = [ task.id for task in hi.pending() if task.name in ["start_balance", "start_resize_pool"] ] logger.debug("Pending balance task.ids = {}".format(pending_task_ids)) try: # https://huey.readthedocs.io/en/latest/api.html#Huey.get # The following does a destructive read unless preserve=True. # This read will also throw the TaskException which is our interest here. # https://huey.readthedocs.io/en/latest/api.html#Huey.result # https://github.com/coleifer/huey/issues/449#issuecomment-535028271 # Syntax requires huey 2.1.3 hi.result(ps.tid) except TaskException as e: ps.status = "failed" # N.B. metadata indexes: retries, traceback, task_id, error ps.message = e.metadata.get("traceback", "missing 'traceback' key") # TODO: Consider a huey signal to triggered the addition of end time # currently no SIGNAL_ERROR is seen to be active (see task.py) # ps.end_time = to.failed # defaults to Null in model. # https://docs.djangoproject.com/en/1.8/ref/models/instances # /#specifying-which-fields-to-save ps.save(update_fields=["status", "message"]) return ps if ps.status == u"started" and ps.tid in pending_task_ids: # Model default (i.e. a new balance) defaults to status "started". # Preserve this state while we await our pending task (1 to 3 seconds). logger.debug("WE CAN LEAVE OUR MODEL AS IS SO RETURN WITH IT") return ps # Get the current status of balance on this pool, irrespective of # a running balance task, ie command line intervention. if ps.internal: cur_status = balance_status_internal(pool) else: cur_status = balance_status(pool) previous_status = {"status": ps.status, "percent_done": ps.percent_done} logger.debug("PREVIOUS_STATUS = {}".format(previous_status)) logger.debug("CURRENT STATUS = {}".format(cur_status)) # TODO: future "Balance Cancel" button should call us to have these # values updated in the db table ready for display later. # Update cur_state to become preferred proposed state. if ( previous_status["status"] == u"cancelling" and cur_status["status"] == u"finished" ): # override current status as 'cancelled' cur_status["status"] = u"cancelled" cur_status["message"] = u"cancelled at {}% complete".format(ps.percent_done) # and retain prior percent finished value cur_status["percent_done"] = ps.percent_done elif ( previous_status["status"] == u"failed" and cur_status["status"] == u"finished" ): # override current status as 'failed' cur_status["status"] = u"failed" # and retain prior percent finished value cur_status["percent_done"] = ps.percent_done logger.debug("PROPOSED STATUS = {}".format(cur_status)) if cur_status == previous_status: logger.debug("PROPOSED STATUS = MODEL STATUS: NO UPDATE REQUIRED") return ps if ( previous_status["status"] != u"finished" and previous_status["status"] != u"cancelled" ): # update the last pool balance status with current status info. logger.debug( "UPDATING BALANCE STATUS ID {} WITH {}".format(ps.id, cur_status) ) PoolBalance.objects.filter(id=ps.id).update(**cur_status) return ps