mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
614 lines
21 KiB
Python
614 lines
21 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
""" P1 tests for VM Schedule
|
|
"""
|
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
|
from marvin.lib.base import Account, ServiceOffering, VirtualMachine, VMSchedule
|
|
from marvin.lib.common import get_domain, get_zone, get_template
|
|
from marvin.lib.utils import cleanup_resources
|
|
|
|
# Import Local Modules
|
|
from nose.plugins.attrib import attr
|
|
|
|
import datetime
|
|
import time
|
|
|
|
|
|
class Services:
|
|
"""Test Snapshots Services"""
|
|
|
|
def __init__(self):
|
|
self.services = {
|
|
"account": {
|
|
"email": "test@test.com",
|
|
"firstname": "Test",
|
|
"lastname": "User",
|
|
"username": "test",
|
|
# Random characters are appended for unique
|
|
# username
|
|
"password": "password",
|
|
},
|
|
"service_offering": {
|
|
"name": "Tiny Instance",
|
|
"displaytext": "Tiny Instance",
|
|
"cpunumber": 1,
|
|
"cpuspeed": 200, # in MHz
|
|
"memory": 256, # In MBs
|
|
},
|
|
"disk_offering": {
|
|
"displaytext": "Small Disk",
|
|
"name": "Small Disk",
|
|
"disksize": 1,
|
|
},
|
|
"server": {
|
|
"displayname": "TestVM",
|
|
"username": "root",
|
|
"password": "password",
|
|
"ssh_port": 22,
|
|
"privateport": 22,
|
|
"publicport": 22,
|
|
"protocol": "TCP",
|
|
},
|
|
"mgmt_server": {
|
|
"ipaddress": "192.168.100.21",
|
|
"username": "root",
|
|
"password": "password",
|
|
"port": 22,
|
|
},
|
|
"templates": {
|
|
"displaytext": "Template",
|
|
"name": "Template",
|
|
"ostype": "CentOS 5.3 (64-bit)",
|
|
"templatefilter": "self",
|
|
},
|
|
"ostype": "CentOS 5.3 (64-bit)",
|
|
}
|
|
|
|
|
|
class TestVMSchedule(cloudstackTestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.testClient = super(TestVMSchedule, cls).getClsTestClient()
|
|
cls.api_client = cls.testClient.getApiClient()
|
|
|
|
cls._cleanup = []
|
|
|
|
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
|
|
|
cls.services = Services().services
|
|
# Get Zone, Domain and templates
|
|
cls.domain = get_domain(cls.api_client)
|
|
cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
|
|
cls.services["mode"] = cls.zone.networktype
|
|
template = get_template(cls.api_client, cls.zone.id, cls.services["ostype"])
|
|
|
|
cls.services["domainid"] = cls.domain.id
|
|
cls.services["server"]["zoneid"] = cls.zone.id
|
|
|
|
cls.services["templates"]["ostypeid"] = template.ostypeid
|
|
cls.services["zoneid"] = cls.zone.id
|
|
|
|
# Create VMs, NAT Rules etc
|
|
cls.account = Account.create(
|
|
cls.api_client, cls.services["account"], domainid=cls.domain.id
|
|
)
|
|
cls._cleanup.append(cls.account)
|
|
|
|
cls.services["account"] = cls.account.name
|
|
|
|
cls.service_offering = ServiceOffering.create(
|
|
cls.api_client, cls.services["service_offering"]
|
|
)
|
|
cls._cleanup.append(cls.service_offering)
|
|
cls.virtual_machine = VirtualMachine.create(
|
|
cls.api_client,
|
|
cls.services["server"],
|
|
templateid=template.id,
|
|
accountid=cls.account.name,
|
|
domainid=cls.account.domainid,
|
|
serviceofferingid=cls.service_offering.id,
|
|
)
|
|
return
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
super(TestVMSchedule, cls).tearDownClass()
|
|
|
|
def setUp(self):
|
|
self.apiclient = self.testClient.getApiClient()
|
|
self.dbclient = self.testClient.getDbConnection()
|
|
self.cleanup = []
|
|
return
|
|
|
|
def tearDown(self):
|
|
super(TestVMSchedule, self).tearDown()
|
|
|
|
@attr(tags=["advanced", "basic"], required_hardware="false")
|
|
def test_01_vmschedule_create(self):
|
|
"""Test VM Schedule Creation in cron format and validate responses"""
|
|
|
|
# Validate the following
|
|
# 1. Create VM Schedule in cron format
|
|
# 2. List VM Schedule and verify the response
|
|
# 3. Delete VM Schedule and verify the response
|
|
|
|
# Create VM Schedule
|
|
schedule = "0 0 1 * *"
|
|
vmschedule = VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
schedule,
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
enabled=True,
|
|
)
|
|
|
|
self.cleanup.append(vmschedule)
|
|
|
|
self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
|
|
|
|
# List VM Schedule
|
|
vmschedules = VMSchedule.list(
|
|
self.apiclient, self.virtual_machine.id, id=vmschedule.id
|
|
)
|
|
|
|
self.assertEqual(
|
|
isinstance(vmschedules, list),
|
|
True,
|
|
"Check list response returns a valid list",
|
|
)
|
|
|
|
self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule list")
|
|
|
|
self.debug("List VM Schedule response: %s" % vmschedules[0].__dict__)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].id,
|
|
vmschedule.id,
|
|
"Check VM Schedule ID in list resources call",
|
|
)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].virtualmachineid,
|
|
self.virtual_machine.id,
|
|
"Check VM ID in list resources call",
|
|
)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].schedule,
|
|
schedule,
|
|
"Check VM Schedule in list resources call",
|
|
)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].timezone,
|
|
str(datetime.datetime.now().astimezone().tzinfo),
|
|
"Check VM Schedule timezone in list resources call",
|
|
)
|
|
|
|
# Check for entry in vm_scheduled_job in db
|
|
vmscheduled_job = self.dbclient.execute(
|
|
"select * from vm_scheduled_job where vm_schedule_id IN (SELECT id FROM vm_schedule WHERE uuid = '%s')"
|
|
% vmschedule.id,
|
|
db="cloud",
|
|
)
|
|
|
|
self.assertIsInstance(
|
|
vmscheduled_job,
|
|
list,
|
|
"Check if VM Schedule exists in vm_scheduled_job table",
|
|
)
|
|
|
|
self.assertGreater(
|
|
len(vmscheduled_job),
|
|
0,
|
|
"Check if VM Schedule exists in vm_scheduled_job table",
|
|
)
|
|
return
|
|
|
|
@attr(tags=["advanced", "basic"], required_hardware="false")
|
|
def test_02_vmschedule_create_parameter_exceptions(self):
|
|
"""Test VM Schedule Creation exceptions with invalid parameters"""
|
|
|
|
# Validate the following
|
|
# 1. Create VM Schedule with invalid virtual machine ID
|
|
# 2. Create VM Schedule with invalid schedule
|
|
# 3. Create VM Schedule with invalid start date
|
|
# 5. Create VM Schedule with invalid action
|
|
# 6. Create VM Schedule with invalid end date
|
|
|
|
# Create VM Schedule with invalid virtual machine ID
|
|
with self.assertRaises(Exception):
|
|
VMSchedule.create(
|
|
self.apiclient,
|
|
"invalid",
|
|
"start",
|
|
"0 0 1 * *",
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
# Create VM Schedule with invalid schedule
|
|
with self.assertRaises(Exception):
|
|
VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
"invalid",
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
# Create VM Schedule with invalid start date
|
|
with self.assertRaises(Exception):
|
|
VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
"0 0 1 * *",
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
"invalid",
|
|
)
|
|
|
|
# Create VM Schedule with invalid action
|
|
with self.assertRaises(Exception):
|
|
VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"invalid",
|
|
"0 0 1 * *",
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
# test invalid end date
|
|
with self.assertRaises(Exception):
|
|
VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
"0 0 1 * *",
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
enddate="invalid",
|
|
)
|
|
return
|
|
|
|
@attr(tags=["advanced", "basic"], required_hardware="false")
|
|
def test_03_vmschedule_update(self):
|
|
"""Test VM Schedule Update in cron format and validate responses"""
|
|
|
|
# Validate the following
|
|
# 1. Create VM Schedule in cron format
|
|
# 2. Update VM Schedule and verify the response
|
|
|
|
# Create VM Schedule
|
|
schedule = "0 0 1 * *"
|
|
vmschedule = VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
schedule,
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
self.cleanup.append(vmschedule)
|
|
|
|
self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
|
|
|
|
# Update VM Schedule
|
|
new_schedule = "0 0 2 * *"
|
|
vmschedule.update(
|
|
self.apiclient,
|
|
id=vmschedule.id,
|
|
virtualmachineid=self.virtual_machine.id,
|
|
description="TestVM",
|
|
schedule=new_schedule,
|
|
timezone=datetime.datetime.now().astimezone().tzinfo,
|
|
startdate=(
|
|
datetime.datetime.now() + datetime.timedelta(minutes=10)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
enddate=(datetime.datetime.now() + datetime.timedelta(hours=10)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
self.debug("Updated VM Schedule with ID: %s" % vmschedule.id)
|
|
|
|
# List VM Schedule
|
|
vmschedules = VMSchedule.list(
|
|
self.apiclient, self.virtual_machine.id, id=vmschedule.id
|
|
)
|
|
self.assertEqual(
|
|
isinstance(vmschedules, list),
|
|
True,
|
|
"Check list response returns a valid list",
|
|
)
|
|
|
|
self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule list")
|
|
|
|
self.debug("List VM Schedule response: %s" % vmschedules[0].__dict__)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].id,
|
|
vmschedule.id,
|
|
"Check VM Schedule ID in list resources call",
|
|
)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].virtualmachineid,
|
|
self.virtual_machine.id,
|
|
"Check VM ID in list resources call",
|
|
)
|
|
|
|
self.assertEqual(
|
|
vmschedules[0].schedule,
|
|
new_schedule,
|
|
"Check VM Schedule in list resources call",
|
|
)
|
|
return
|
|
|
|
@attr(tags=["advanced", "basic"], required_hardware="false")
|
|
def test_04_vmschedule_update_parameter_exceptions(self):
|
|
"""Test VM Schedule Update exceptions with invalid parameters"""
|
|
|
|
# Validate the following
|
|
# 1. Update VM Schedule with invalid schedule
|
|
# 2. Update VM Schedule with invalid start date
|
|
# 3. Update VM Schedule with invalid ID
|
|
# 4. Update VM Schedule with invalid end date
|
|
|
|
# Create VM Schedule
|
|
schedule = "0 0 1 * *"
|
|
vmschedule = VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
schedule,
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
)
|
|
|
|
self.cleanup.append(vmschedule)
|
|
|
|
self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
|
|
|
|
# Update VM Schedule with invalid schedule
|
|
with self.assertRaises(Exception):
|
|
vmschedule.update(
|
|
self.apiclient,
|
|
id=vmschedule.id,
|
|
virtualmachineid=self.virtual_machine.id,
|
|
description="TestVM",
|
|
schedule="invalid",
|
|
timezone=datetime.datetime.now().astimezone().tzinfo,
|
|
startdate=(
|
|
datetime.datetime.now() + datetime.timedelta(minutes=5)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
)
|
|
|
|
# Update VM Schedule with invalid start date
|
|
with self.assertRaises(Exception):
|
|
vmschedule.update(
|
|
self.apiclient,
|
|
id=vmschedule.id,
|
|
virtualmachineid=self.virtual_machine.id,
|
|
description="TestVM",
|
|
schedule=schedule,
|
|
timezone=datetime.datetime.now().astimezone().tzinfo,
|
|
startdate=(
|
|
datetime.datetime.now() - datetime.timedelta(days=1)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
)
|
|
|
|
# Update VM Schedule with invalid ID
|
|
with self.assertRaises(Exception):
|
|
vmschedule.update(
|
|
self.apiclient,
|
|
id="invalid",
|
|
virtualmachineid=self.virtual_machine.id,
|
|
description="TestVM",
|
|
schedule=schedule,
|
|
timezone=datetime.datetime.now().astimezone().tzinfo,
|
|
startdate=(
|
|
datetime.datetime.now() + datetime.timedelta(minutes=5)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
)
|
|
|
|
# Update VM Schedule with invalid end date
|
|
with self.assertRaises(Exception):
|
|
vmschedule.update(
|
|
self.apiclient,
|
|
id=vmschedule.id,
|
|
virtualmachineid=self.virtual_machine.id,
|
|
description="TestVM",
|
|
schedule=schedule,
|
|
timezone=datetime.datetime.now().astimezone().tzinfo,
|
|
startdate=(
|
|
datetime.datetime.now() + datetime.timedelta(minutes=5)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
enddate=(
|
|
datetime.datetime.now() - datetime.timedelta(minutes=5)
|
|
).strftime("%Y-%m-%d %H:%M:%S"),
|
|
)
|
|
|
|
return
|
|
|
|
@attr(tags=["advanced", "basic"], required_hardware="false")
|
|
def test_05_vmschedule_test_e2e(self):
|
|
# Validate the following
|
|
# 1. Create 2 VM Schedules - start and stop
|
|
# 2. Verify VM Schedule is created
|
|
# 3. Verify VM is stopped after schedule time
|
|
# 4. Verify VM is started after schedule time
|
|
# 5. Delete VM Schedule
|
|
# 6. Verify VM Schedule is deleted
|
|
# 7. Verify VM is not stopped after schedule time
|
|
# 8. Verify VM is not started after schedule time
|
|
|
|
# Create VM Schedule - start
|
|
start_schedule = "*/2 * * * *"
|
|
start_vmschedule = VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"start",
|
|
start_schedule,
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(seconds=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
enabled=True,
|
|
)
|
|
|
|
self.debug("Created VM Schedule with ID: %s" % start_vmschedule.id)
|
|
|
|
# Create VM Schedule - stop
|
|
stop_schedule = "*/1 * * * *"
|
|
stop_vmschedule = VMSchedule.create(
|
|
self.apiclient,
|
|
self.virtual_machine.id,
|
|
"stop",
|
|
stop_schedule,
|
|
datetime.datetime.now().astimezone().tzinfo,
|
|
# Current date minutes in format "2014-01-01 00:00:00"
|
|
(datetime.datetime.now() + datetime.timedelta(seconds=5)).strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
),
|
|
enabled=True,
|
|
)
|
|
|
|
self.debug("Created VM Schedule with ID: %s" % stop_vmschedule.id)
|
|
|
|
# Verify VM Schedule is created
|
|
vmschedules = VMSchedule.list(
|
|
self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
|
|
)
|
|
|
|
self.assertEqual(
|
|
isinstance(vmschedules, list),
|
|
True,
|
|
"Check list response returns a valid list",
|
|
)
|
|
self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule is created")
|
|
|
|
# poll every 10 seconds (max waiting time is 6 minutes) and check VM's state for changes
|
|
previous_state = self.virtual_machine.state
|
|
self.debug("VM state: %s" % self.virtual_machine.state)
|
|
is_stop_schedule_working = False
|
|
is_start_schedule_working = False
|
|
for i in range(0, 36):
|
|
time.sleep(10)
|
|
current_state = self.virtual_machine.update(self.apiclient).state
|
|
self.debug("Polling VM state: %s" % current_state)
|
|
if previous_state in ("Running", "Starting") and current_state in (
|
|
"Stopped",
|
|
"Stopping",
|
|
):
|
|
is_stop_schedule_working = True
|
|
elif previous_state in ("Stopped", "Stopping") and current_state in (
|
|
"Running",
|
|
"Starting",
|
|
):
|
|
is_start_schedule_working = True
|
|
if is_start_schedule_working and is_stop_schedule_working:
|
|
break
|
|
previous_state = current_state
|
|
|
|
self.debug("Is stop schedule working: %s" % is_stop_schedule_working)
|
|
self.debug("Is start schedule working: %s" % is_start_schedule_working)
|
|
|
|
self.assertTrue(
|
|
is_stop_schedule_working,
|
|
"VM switched states from Running to Stopped at least once",
|
|
)
|
|
|
|
self.assertTrue(
|
|
is_start_schedule_working,
|
|
"VM switched states from Stopped to Running at least once",
|
|
)
|
|
|
|
# Delete VM Schedule
|
|
start_vmschedule.delete(self.apiclient)
|
|
stop_vmschedule.delete(self.apiclient)
|
|
|
|
# To ensure that all vm schedules have been deleted and all of their jobs have been completed
|
|
time.sleep(60)
|
|
|
|
# Verify VM Schedule is deleted
|
|
self.assertEqual(
|
|
VMSchedule.list(
|
|
self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
|
|
),
|
|
None,
|
|
"Check VM Schedule is deleted",
|
|
)
|
|
self.assertEqual(
|
|
VMSchedule.list(
|
|
self.apiclient, self.virtual_machine.id, id=stop_vmschedule.id
|
|
),
|
|
None,
|
|
"Check VM Schedule is deleted",
|
|
)
|
|
|
|
# Verify VM does not switch states after deleting schedules at least for 2 minutes
|
|
previous_state = self.virtual_machine.update(self.apiclient).state
|
|
state_changed = False
|
|
for i in range(0, 4):
|
|
time.sleep(30)
|
|
current_state = self.virtual_machine.update(self.apiclient).state
|
|
if previous_state != current_state:
|
|
# Add these checks because VMs can take some time to start or stop
|
|
if (previous_state == 'Starting' and current_state in ('Starting', 'Running')) or (
|
|
previous_state == 'Stopping' and current_state in ('Stopping', 'Stopped')):
|
|
continue
|
|
self.debug(
|
|
"VM changed state from %s to %s" % (previous_state, current_state)
|
|
)
|
|
state_changed = True
|
|
break
|
|
|
|
self.assertFalse(
|
|
state_changed,
|
|
"VM did not switch states after schedule time",
|
|
)
|
|
return
|