| # 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 |