Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions tools/marvin/marvin/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@
'''
ALLOCATED = "Allocated"

'''
Host states
'''
HOST_CREATING = "Creating"
HOST_CONNECTING = "Connecting"
HOST_UP = "Up"
HOST_DOWN = "Down"
HOST_DISCONNECTED = "Disconnected"
HOST_ALERT = "Alert"
HOST_REMOVED = "Removed"
HOST_ERROR = "Error"
HOST_REBALANCING = "Rebalancing"
HOST_UNKNOWN = "Unknown"

'''
Host resource states
'''
HOST_RS_CREATING = "Creating"
HOST_RS_ENABLED = "Enabled"
HOST_RS_DISABLED = "Disabled"
HOST_RS_PREPARE_FOR_MAINTENANCE = "PrepareForMaintenance"
HOST_RS_ERROR_IN_MAINTENANCE = "ErrorInMaintenance"
HOST_RS_MAINTENANCE = "Maintenance"
HOST_RS_ERROR = "Error"

'''
Storage Tags
'''
Expand Down
36 changes: 32 additions & 4 deletions tools/marvin/marvin/lib/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
from marvin.cloudstackAPI import *
from marvin.codes import (FAILED, FAIL, PASS, RUNNING, STOPPED,
STARTING, DESTROYED, EXPUNGING,
STOPPING, BACKED_UP, BACKING_UP)
STOPPING, BACKED_UP, BACKING_UP,
HOST_RS_MAINTENANCE)
from marvin.cloudstackException import GetDetailExceptionInfo, CloudstackAPIException
from marvin.lib.utils import validateList, is_server_ssh_ready, random_gen
from marvin.lib.utils import validateList, is_server_ssh_ready, random_gen, wait_until
# Import System modules
import time
import hashlib
Expand Down Expand Up @@ -2459,13 +2460,40 @@ def create(cls, apiclient, cluster, services, zoneid=None, podid=None, hyperviso
GetDetailExceptionInfo(e)
return FAILED

@staticmethod
def _check_resource_state(apiclient, hostid, resourcestate):
hosts = Host.list(apiclient, id=hostid, listall=True)

validationresult = validateList(hosts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if hosts is None or empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateList should catch those issues.


assert validationresult is not None, "'validationresult' should not be equal to 'None'."

assert isinstance(validationresult, list), "'validationresult' should be a 'list'."

assert len(validationresult) == 3, "'validationresult' should be a list with three items in it."

if validationresult[0] == FAIL:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if validationresult is None?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hear you, @jburwell, but the API description points out that the return type is a List with three items (and what each item indicates). We can certainly validate return types, but it adds overhead (both in terms of execution time and extra logic clouding things up) for limited value here (it will just throw an exception if the value happened to be None).

Thoughts on that?

Copy link
Contributor

@jburwell jburwell May 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mike-tutkowski yes, the exception will fail the test. However, the result is an unfriendly stack trace that does not explain the context and expectations of the failure. I suggest asserting these conditions with a messaging explaining the expectations (e.g. "Expected to find a host with an id of %d".format(hostid)).

I don't feel this issue is enough to hold back the PR. However, I think if we consistently asserted on our expectations/assumptions, test failures would be easier to comprehend and diagnose. Not only does the practice provide a human readable explication, but it also fails fast. All too often, tests failures surface side-effects of failed assumptions multiple steps before the error occurred.

(Apologies for the mini PR rant)

raise Exception("Host list validation failed: %s" % validationresult[2])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if validationresult does not contain 3 elements?

Copy link
Member Author

@mike-tutkowski mike-tutkowski May 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding "What if validationresult is None."


if str(hosts[0].resourcestate).lower().decode("string_escape") == str(resourcestate).lower():
return True, None

return False, "Host is not in the following state: " + str(resourcestate)

def delete(self, apiclient):
"""Delete Host"""
# Host must be in maintenance mode before deletion
cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd()
cmd.id = self.id
apiclient.prepareHostForMaintenance(cmd)
time.sleep(30)

retry_interval = 10
num_tries = 10

wait_result, return_val = wait_until(retry_interval, num_tries, Host._check_resource_state, apiclient, self.id, HOST_RS_MAINTENANCE)

if not wait_result:
raise Exception(return_val)

cmd = deleteHost.deleteHostCmd()
cmd.id = self.id
Expand Down Expand Up @@ -2711,7 +2739,7 @@ def getState(cls, apiclient, poolid, state, timeout=600):
id=poolid, listAll=True)
validationresult = validateList(pools)
if validationresult[0] == FAIL:
raise Exception("Host list validation failed: %s" % validationresult[2])
raise Exception("Pool list validation failed: %s" % validationresult[2])
elif str(pools[0].state).lower().decode("string_escape") == str(state).lower():
returnValue = [PASS, None]
break
Expand Down