integration test updated and delete method for cluster/node
This commit is contained in:
parent
6557dc18e4
commit
458bb51a36
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
|||||||
/.vagrant
|
/.vagrant
|
||||||
|
|
||||||
/build
|
/build
|
||||||
|
/test/nosetests.xml
|
||||||
|
@ -193,7 +193,7 @@ class ClusterCollectionHandler(BaseHandler):
|
|||||||
|
|
||||||
class ClusterHandler(JSONHandler):
|
class ClusterHandler(JSONHandler):
|
||||||
|
|
||||||
allowed_methods = ('GET', 'PUT')
|
allowed_methods = ('GET', 'PUT', 'DELETE')
|
||||||
model = Cluster
|
model = Cluster
|
||||||
fields = ('id', 'name')
|
fields = ('id', 'name')
|
||||||
special_fields = ('nodes', 'release', 'task')
|
special_fields = ('nodes', 'release', 'task')
|
||||||
@ -241,6 +241,14 @@ class ClusterHandler(JSONHandler):
|
|||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
return rc.NOT_FOUND
|
return rc.NOT_FOUND
|
||||||
|
|
||||||
|
def delete(self, request, cluster_id):
|
||||||
|
try:
|
||||||
|
cluster = Cluster.objects.get(id=cluster_id)
|
||||||
|
cluster.delete()
|
||||||
|
return rc.DELETED
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return rc.NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
class NodeCollectionHandler(BaseHandler):
|
class NodeCollectionHandler(BaseHandler):
|
||||||
|
|
||||||
@ -269,7 +277,7 @@ class NodeCollectionHandler(BaseHandler):
|
|||||||
|
|
||||||
class NodeHandler(JSONHandler):
|
class NodeHandler(JSONHandler):
|
||||||
|
|
||||||
allowed_methods = ('GET', 'PUT')
|
allowed_methods = ('GET', 'PUT', 'DELETE')
|
||||||
model = Node
|
model = Node
|
||||||
fields = ('id', 'name', 'metadata', 'status', 'mac', 'fqdn', 'ip',
|
fields = ('id', 'name', 'metadata', 'status', 'mac', 'fqdn', 'ip',
|
||||||
'redeployment_needed')
|
'redeployment_needed')
|
||||||
@ -307,6 +315,14 @@ class NodeHandler(JSONHandler):
|
|||||||
node.save()
|
node.save()
|
||||||
return NodeHandler.render(node)
|
return NodeHandler.render(node)
|
||||||
|
|
||||||
|
def delete(self, request, node_id):
|
||||||
|
try:
|
||||||
|
node = Node.objects.get(id=node_id)
|
||||||
|
node.delete()
|
||||||
|
return rc.DELETED
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return rc.NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
class AttributeCollectionHandler(BaseHandler):
|
class AttributeCollectionHandler(BaseHandler):
|
||||||
|
|
||||||
@ -531,7 +547,7 @@ class ReleaseCollectionHandler(BaseHandler):
|
|||||||
|
|
||||||
class ReleaseHandler(JSONHandler):
|
class ReleaseHandler(JSONHandler):
|
||||||
|
|
||||||
allowed_methods = ('GET',)
|
allowed_methods = ('GET', 'DELETE')
|
||||||
model = Release
|
model = Release
|
||||||
fields = ('id', 'name', 'version', 'description', 'networks_metadata')
|
fields = ('id', 'name', 'version', 'description', 'networks_metadata')
|
||||||
special_fields = ('roles',)
|
special_fields = ('roles',)
|
||||||
@ -554,6 +570,14 @@ class ReleaseHandler(JSONHandler):
|
|||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
return rc.NOT_FOUND
|
return rc.NOT_FOUND
|
||||||
|
|
||||||
|
def delete(self, request, release_id):
|
||||||
|
try:
|
||||||
|
release = Release.objects.get(id=release_id)
|
||||||
|
release.delete()
|
||||||
|
return rc.DELETED
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return rc.NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
class NetworkHandler(JSONHandler):
|
class NetworkHandler(JSONHandler):
|
||||||
|
|
||||||
|
@ -137,11 +137,6 @@ def deploy_cluster(cluster_id):
|
|||||||
add_attrs = {}
|
add_attrs = {}
|
||||||
|
|
||||||
roles_for_node = node.roles.all()
|
roles_for_node = node.roles.all()
|
||||||
# TODO(mihgen): It should be possible to have node w/o role assigned
|
|
||||||
if not roles_for_node:
|
|
||||||
message = "Task %s failed: Roles list for node %s is empty" \
|
|
||||||
% (deploy_cluster.request.id, node.id)
|
|
||||||
raise EmptyListError(message)
|
|
||||||
|
|
||||||
node_json['cluster_id'] = cluster_id
|
node_json['cluster_id'] = cluster_id
|
||||||
for f in node._meta.fields:
|
for f in node._meta.fields:
|
||||||
|
@ -154,6 +154,13 @@ class TestHandlers(TestCase):
|
|||||||
self.another_node.id)
|
self.another_node.id)
|
||||||
cluster = clusters_from_db[0]
|
cluster = clusters_from_db[0]
|
||||||
self.assertEquals(len(cluster.release.networks.all()), 3)
|
self.assertEquals(len(cluster.release.networks.all()), 3)
|
||||||
|
# test delete
|
||||||
|
resp = self.client.delete(
|
||||||
|
reverse('cluster_handler', kwargs={'cluster_id': cluster.id}),
|
||||||
|
"",
|
||||||
|
"application/json"
|
||||||
|
)
|
||||||
|
self.assertEquals(resp.status_code, 204)
|
||||||
|
|
||||||
def test_cluster_update(self):
|
def test_cluster_update(self):
|
||||||
updated_name = 'Updated cluster'
|
updated_name = 'Updated cluster'
|
||||||
@ -207,6 +214,14 @@ class TestHandlers(TestCase):
|
|||||||
nodes_from_db = Node.objects.filter(id=node_id)
|
nodes_from_db = Node.objects.filter(id=node_id)
|
||||||
self.assertEquals(len(nodes_from_db), 1)
|
self.assertEquals(len(nodes_from_db), 1)
|
||||||
|
|
||||||
|
# test delete
|
||||||
|
resp = self.client.delete(
|
||||||
|
reverse('node_handler', kwargs={'node_id': node_id}),
|
||||||
|
"",
|
||||||
|
"application/json"
|
||||||
|
)
|
||||||
|
self.assertEquals(resp.status_code, 204)
|
||||||
|
|
||||||
def test_node_creation_using_put(self):
|
def test_node_creation_using_put(self):
|
||||||
node_id = '080000000002'
|
node_id = '080000000002'
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class SSHClient(object):
|
|||||||
|
|
||||||
|
|
||||||
def exec_cmd(self, command):
|
def exec_cmd(self, command):
|
||||||
logging.info("Executing command: '%s'" % command)
|
logging.info("Executing command: '%s'" % command.rstrip())
|
||||||
chan = self.ssh_client.get_transport().open_session()
|
chan = self.ssh_client.get_transport().open_session()
|
||||||
stdin = chan.makefile('wb')
|
stdin = chan.makefile('wb')
|
||||||
stdout = chan.makefile('rb')
|
stdout = chan.makefile('rb')
|
||||||
|
@ -21,7 +21,8 @@ SOLO_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "scripts", "agen
|
|||||||
DEPLOY_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "bin", "deploy")
|
DEPLOY_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "bin", "deploy")
|
||||||
COOKBOOKS_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "cookbooks")
|
COOKBOOKS_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "cookbooks")
|
||||||
SAMPLE_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "scripts", "ci")
|
SAMPLE_PATH = os.path.join(os.path.dirname(__file__), "..", "..", "scripts", "ci")
|
||||||
SAMPLE_REMOTE_PATH = "/root"
|
SAMPLE_REMOTE_PATH = "/home/ubuntu"
|
||||||
|
|
||||||
|
|
||||||
class StillPendingException(Exception):
|
class StillPendingException(Exception):
|
||||||
pass
|
pass
|
||||||
@ -34,16 +35,18 @@ class TestNode(TestCase):
|
|||||||
self.remote = SSHClient()
|
self.remote = SSHClient()
|
||||||
|
|
||||||
def test_node(self):
|
def test_node(self):
|
||||||
cookbook_remote_path = os.path.join(SAMPLE_REMOTE_PATH, "sample-cook")
|
|
||||||
release_remote_path = os.path.join(SAMPLE_REMOTE_PATH, "sample-release.json")
|
|
||||||
|
|
||||||
logging.info("Starting slave node")
|
|
||||||
admin_node = ci.environment.node['admin']
|
admin_node = ci.environment.node['admin']
|
||||||
admin_ip = admin_node.ip_address
|
admin_ip = str(admin_node.ip_address)
|
||||||
node = ci.environment.node['slave']
|
slave = ci.environment.node['slave']
|
||||||
node.start()
|
slave_id = slave.interfaces[0].mac_address.replace(":", "").upper()
|
||||||
|
logging.info("Starting slave node")
|
||||||
|
slave.start()
|
||||||
|
|
||||||
slave_id = node.interfaces[0].mac_address.replace(":", "").upper()
|
self._load_sample_admin(
|
||||||
|
host=admin_ip,
|
||||||
|
user="ubuntu",
|
||||||
|
passwd="r00tme"
|
||||||
|
)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
logging.info("Waiting for slave agent to run...")
|
logging.info("Waiting for slave agent to run...")
|
||||||
@ -56,9 +59,16 @@ class TestNode(TestCase):
|
|||||||
else:
|
else:
|
||||||
time.sleep(15)
|
time.sleep(15)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cluster = json.loads(self.client.get(
|
||||||
|
"http://%s:8000/api/clusters/1" % admin_ip,
|
||||||
|
log=True
|
||||||
|
))
|
||||||
|
except ValueError:
|
||||||
|
logging.info("No clusters found - creating test cluster...")
|
||||||
cluster = json.loads(self.client.post(
|
cluster = json.loads(self.client.post(
|
||||||
"http://%s:8000/api/clusters" % admin_ip,
|
"http://%s:8000/api/clusters" % admin_ip,
|
||||||
data='{ "name": "MyOwnPrivateCluster", "release": 1 }',
|
data='{ "name": "MyOwnPrivateCluster", "release": 2 }',
|
||||||
log=True
|
log=True
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -75,7 +85,7 @@ class TestNode(TestCase):
|
|||||||
|
|
||||||
resp = json.loads(self.client.put(
|
resp = json.loads(self.client.put(
|
||||||
"http://%s:8000/api/nodes/%s" % (admin_ip, slave_id),
|
"http://%s:8000/api/nodes/%s" % (admin_ip, slave_id),
|
||||||
data='{ "new_roles": [1, 2], "redeployment_needed": true }'
|
data='{ "new_roles": [2, 3], "redeployment_needed": true }'
|
||||||
))
|
))
|
||||||
if len(resp["new_roles"]) == 0:
|
if len(resp["new_roles"]) == 0:
|
||||||
raise ValueError("Failed to assign roles to node")
|
raise ValueError("Failed to assign roles to node")
|
||||||
@ -87,13 +97,13 @@ class TestNode(TestCase):
|
|||||||
))
|
))
|
||||||
task_id = task['task_id']
|
task_id = task['task_id']
|
||||||
logging.info("Task created: %s" % task_id)
|
logging.info("Task created: %s" % task_id)
|
||||||
logging.info("Waiting for completion of admin node software installation")
|
logging.info("Waiting for completion of slave node software installation")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
task = json.loads(self.client.get(
|
task = json.loads(self.client.get(
|
||||||
"http://%s:8000/api/tasks/%s/" % (admin_ip, task_id)
|
"http://%s:8000/api/tasks/%s/" % (admin_ip, task_id)
|
||||||
))
|
))
|
||||||
self.check_tasks(task)
|
self._check_tasks(task)
|
||||||
break
|
break
|
||||||
except StillPendingException:
|
except StillPendingException:
|
||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
@ -106,58 +116,15 @@ class TestNode(TestCase):
|
|||||||
wait(lambda: tcp_ping(node["ip"], 22), timeout=1800)
|
wait(lambda: tcp_ping(node["ip"], 22), timeout=1800)
|
||||||
self.remote.connect_ssh(node["ip"], "root", "r00tme")
|
self.remote.connect_ssh(node["ip"], "root", "r00tme")
|
||||||
|
|
||||||
self.remote.rmdir(cookbook_remote_path)
|
|
||||||
self.remote.rmdir(os.path.join(SAMPLE_REMOTE_PATH, "cookbooks"))
|
|
||||||
self.remote.rmdir(os.path.join(SAMPLE_REMOTE_PATH, "solo"))
|
|
||||||
|
|
||||||
self.remote.scp(
|
|
||||||
os.path.join(SAMPLE_PATH, "sample-release.json"),
|
|
||||||
release_remote_path
|
|
||||||
)
|
|
||||||
|
|
||||||
self.remote.mkdir(os.path.join(SAMPLE_REMOTE_PATH, "solo"))
|
|
||||||
self.remote.mkdir(os.path.join(SAMPLE_REMOTE_PATH, "solo/config"))
|
|
||||||
|
|
||||||
self.remote.scp(
|
|
||||||
DEPLOY_PATH,
|
|
||||||
os.path.join(SAMPLE_REMOTE_PATH, "deploy")
|
|
||||||
)
|
|
||||||
self.remote.scp(
|
|
||||||
os.path.join(SOLO_PATH, "solo.json"),
|
|
||||||
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.json")
|
|
||||||
)
|
|
||||||
self.remote.scp(
|
|
||||||
os.path.join(SOLO_PATH, "solo.rb"),
|
|
||||||
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.rb")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.remote.scp_d(
|
|
||||||
os.path.join(SAMPLE_PATH, "sample-cook"),
|
|
||||||
SAMPLE_REMOTE_PATH
|
|
||||||
)
|
|
||||||
self.remote.scp_d(
|
|
||||||
COOKBOOKS_PATH,
|
|
||||||
SAMPLE_REMOTE_PATH
|
|
||||||
)
|
|
||||||
|
|
||||||
self.remote.exec_cmd("rm /tmp/chef_success")
|
|
||||||
result = self.remote.exec_cmd("chef-solo -l debug -c %s -j %s" % (
|
|
||||||
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.rb"),
|
|
||||||
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.json")
|
|
||||||
))
|
|
||||||
if result['exit_status'] != 0:
|
|
||||||
self.remote.disconnect()
|
|
||||||
raise Exception("Error while executing chef-solo: %s" % str(result))
|
|
||||||
|
|
||||||
# check if recipes executed
|
# check if recipes executed
|
||||||
ret = self.remote.exec_cmd("test -f /tmp/chef_success && echo 'SUCCESS'")
|
ret = self.remote.exec_cmd("test -f /tmp/chef_success")
|
||||||
if not "SUCCESS" in ret.split("\r\n")[1:]:
|
if ret['exit_status'] != 0:
|
||||||
raise Exception("Recipe failed to execute!")
|
raise Exception("Recipes failed to execute!")
|
||||||
# check recipes execution order
|
# check recipes execution order
|
||||||
ret = self.remote.exec_cmd("cat /tmp/chef_success")
|
ret = self.remote.exec_cmd("cat /tmp/chef_success")
|
||||||
if not ret.split("\r\n")[1:-1] == ['monitor', 'default', 'compute']:
|
if not ret['stdout'].split("\r\n") == ['monitor', 'default', 'compute']:
|
||||||
raise Exception("Recipes executed in a wrong order: %s!" \
|
raise Exception("Recipes executed in a wrong order: %s!" \
|
||||||
% str(ret.split("\r\n")[1:-1]))
|
% str(ret['stdout'].split("\r\n")))
|
||||||
"""
|
"""
|
||||||
# check passwords
|
# check passwords
|
||||||
self.remote.exec_cmd("tar -C %s -xvf /root/nodes.tar.gz" % SAMPLE_REMOTE_PATH)
|
self.remote.exec_cmd("tar -C %s -xvf /root/nodes.tar.gz" % SAMPLE_REMOTE_PATH)
|
||||||
@ -170,7 +137,7 @@ class TestNode(TestCase):
|
|||||||
|
|
||||||
self.remote.disconnect()
|
self.remote.disconnect()
|
||||||
|
|
||||||
def check_tasks(self, task):
|
def _check_tasks(self, task):
|
||||||
if task['status'] != 'SUCCESS':
|
if task['status'] != 'SUCCESS':
|
||||||
if task['status'] == 'PENDING':
|
if task['status'] == 'PENDING':
|
||||||
raise StillPendingException("Task %s is still pending")
|
raise StillPendingException("Task %s is still pending")
|
||||||
@ -180,4 +147,50 @@ class TestNode(TestCase):
|
|||||||
)
|
)
|
||||||
if 'subtasks' in task and task['subtasks']:
|
if 'subtasks' in task and task['subtasks']:
|
||||||
for subtask in task['subtasks']:
|
for subtask in task['subtasks']:
|
||||||
self.check_tasks(subtask)
|
self._check_tasks(subtask)
|
||||||
|
|
||||||
|
def _load_sample_admin(self, host, user, passwd):
|
||||||
|
cookbook_remote_path = os.path.join(SAMPLE_REMOTE_PATH, "sample-cook")
|
||||||
|
release_remote_path = os.path.join(SAMPLE_REMOTE_PATH, "sample-release.json")
|
||||||
|
self.remote.connect_ssh(host, user, passwd)
|
||||||
|
self.remote.rmdir(cookbook_remote_path)
|
||||||
|
self.remote.rmdir(os.path.join(SAMPLE_REMOTE_PATH, "cookbooks"))
|
||||||
|
self.remote.rmdir(os.path.join(SAMPLE_REMOTE_PATH, "solo"))
|
||||||
|
self.remote.scp(
|
||||||
|
os.path.join(SAMPLE_PATH, "sample-release.json"),
|
||||||
|
release_remote_path
|
||||||
|
)
|
||||||
|
self.remote.mkdir(os.path.join(SAMPLE_REMOTE_PATH, "solo"))
|
||||||
|
self.remote.mkdir(os.path.join(SAMPLE_REMOTE_PATH, "solo/config"))
|
||||||
|
self.remote.scp(
|
||||||
|
os.path.join(SOLO_PATH, "solo.json"),
|
||||||
|
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.json")
|
||||||
|
)
|
||||||
|
self.remote.scp(
|
||||||
|
os.path.join(SOLO_PATH, "solo.rb"),
|
||||||
|
os.path.join(SAMPLE_REMOTE_PATH, "solo", "config", "solo.rb")
|
||||||
|
)
|
||||||
|
self.remote.scp_d(
|
||||||
|
os.path.join(SAMPLE_PATH, "sample-cook"),
|
||||||
|
SAMPLE_REMOTE_PATH
|
||||||
|
)
|
||||||
|
self.remote.scp_d(
|
||||||
|
COOKBOOKS_PATH,
|
||||||
|
SAMPLE_REMOTE_PATH
|
||||||
|
)
|
||||||
|
commands = [
|
||||||
|
#"rm -rf /opt/nailgun/nailgun.sqlite",
|
||||||
|
#"/opt/nailgun-venv/bin/python /opt/nailgun/manage.py syncdb --noinput",
|
||||||
|
#"chown nailgun:nailgun /opt/nailgun/nailgun.sqlite",
|
||||||
|
"/opt/nailgun/bin/install_cookbook %s" % cookbook_remote_path,
|
||||||
|
"/opt/nailgun/bin/create_release %s" % release_remote_path
|
||||||
|
]
|
||||||
|
|
||||||
|
with self.remote.sudo:
|
||||||
|
for cmd in commands:
|
||||||
|
res = self.remote.exec_cmd(cmd)
|
||||||
|
if res['exit_status'] != 0:
|
||||||
|
self.remote.disconnect()
|
||||||
|
raise Exception("Command failed: %s" % str(res))
|
||||||
|
|
||||||
|
self.remote.disconnect()
|
Loading…
Reference in New Issue
Block a user