diff --git a/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/HostingSubscription_isUpgradable.xml b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/HostingSubscription_isUpgradable.xml new file mode 100644 index 0000000000000000000000000000000000000000..5f38e373bbcb7383b0ac27518eda636db03e19c1 --- /dev/null +++ b/master/bt5/slapos_cloud/SkinTemplateItem/portal_skins/slapos_cloud/HostingSubscription_isUpgradable.xml @@ -0,0 +1,102 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <global name="PythonScript" module="Products.PythonScripts.PythonScript"/> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>Script_magic</string> </key> + <value> <int>3</int> </value> + </item> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_container</string> </key> + <value> <string>container</string> </value> + </item> + <item> + <key> <string>name_context</string> </key> + <value> <string>context</string> </value> + </item> + <item> + <key> <string>name_m_self</string> </key> + <value> <string>script</string> </value> + </item> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>_body</string> </key> + <value> <string>hosting_subscription = context\n +portal = context.getPortalObject()\n +\n +slap_state = [\'start_requested\', \'stop_requested\']\n +\n +if not hosting_subscription.getSlapState() in slap_state:\n + return False\n +\n +source_instance = hosting_subscription.getPredecessorValue()\n +if not source_instance or source_instance.getSlapState() not in slap_state:\n + return False\n +\n +latest_software_url = hosting_subscription.HostingSubscription_getNewerSofwareRelease()\n +if latest_software_url == "":\n + return False\n +\n +computer = source_instance.getAggregateValue().getParentValue()\n +if computer.getValidationState() != \'validated\':\n + return False\n +isUpgradable = True\n + \n +#Find Software Installation\n +software_installation_list = portal.portal_catalog(\n + portal_type="Software Installation",\n + validation_state="validated",\n + url_string=latest_software_url,\n + default_aggregate_uid=computer.getUid(),\n + #XXX - don\'t select destroyed Software Installation\n + slap_state=\'start_requested\'\n + )\n +# check again slap_state because it might be ignored in previous request!\n +if not \'start_requested\' in [software_installation.getSlapState() \\\n + for software_installation in software_installation_list]:\n + isUpgradable = False\n +\n +return isUpgradable\n +</string> </value> + </item> + <item> + <key> <string>_params</string> </key> + <value> <string></string> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>HostingSubscription_isUpgradable</string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/master/bt5/slapos_cloud/TestTemplateItem/testSlapOSSoftwareProduct.py b/master/bt5/slapos_cloud/TestTemplateItem/testSlapOSSoftwareProduct.py index d84bacaf061b4383bc73588decbdf50eb9f42918..31fe57b35ab600699a11f867df537cb2f34baa2c 100644 --- a/master/bt5/slapos_cloud/TestTemplateItem/testSlapOSSoftwareProduct.py +++ b/master/bt5/slapos_cloud/TestTemplateItem/testSlapOSSoftwareProduct.py @@ -4,6 +4,128 @@ from AccessControl import getSecurityManager from DateTime import DateTime import transaction +class BaseTestSlapOSMixin(testSlapOSMixin): + + def afterSetUp(self): + super(BaseTestSlapOSMixin, self).afterSetUp() + self.request_kw = dict( + software_title=self.generateNewSoftwareTitle(), + software_type=self.generateNewSoftwareType(), + instance_xml=self.generateSafeXml(), + sla_xml=self.generateEmptyXml(), + shared=False, + state="started" + ) + + def _makePerson(self, new_id): + # Clone computer document + person = self.portal.person_module.template_member\ + .Base_createCloneDocument(batch_mode=1) + person.edit( + title="live_test_%s" % new_id, + reference='TESTPERSON-%s' % new_id, + default_email_text="live_test_%s@example.org" % new_id, + ) + + person.validate() + for assignment in person.contentValues(portal_type="Assignment"): + assignment.open() + transaction.commit() + + return person + + def _makeComputer(self,new_id): + # Clone computer document + person = self.portal.person_module.template_member\ + .Base_createCloneDocument(batch_mode=1) + computer = self.portal.computer_module\ + .template_computer.Base_createCloneDocument(batch_mode=1) + computer.edit( + title="computer ticket %s" % (new_id, ), + reference="TESTCOMPT-%s" % (new_id, ), + source_administration_value=person + ) + computer.validate() + + return computer + + def _makeComputerPartitions(self, computer): + for i in range(1, 5): + id_ = 'partition%s' % (i, ) + p = computer.newContent(portal_type='Computer Partition', + id=id_, + title=id_, + reference=id_, + default_network_address_ip_address='ip_address_%s' % i, + default_network_address_netmask='netmask_%s' % i) + p.markFree() + p.validate + + def _markComputerPartitionBusy(self, computer, software_instance): + for partition in computer.contentValues(portal_type='Computer Partition'): + if partition.getSlapState() == 'free': + software_instance.edit(aggregate=partition.getRelativeUrl()) + break; + + def _makeSoftwareInstallation(self, new_id, computer, software_release_url): + software_installation = self.portal\ + .software_installation_module.template_software_installation\ + .Base_createCloneDocument(batch_mode=1) + software_installation.edit( + url_string=software_release_url, + aggregate=computer.getRelativeUrl(), + reference='TESTSOFTINSTS-%s' % new_id, + title='Start requested for %s' % computer.getUid() + ) + software_installation.validate() + software_installation.requestStart() + + return software_installation + + def _makeHostingSubscription(self, new_id, software_url="", person=None): + if not person: + person = self._makePerson(new_id) + + hosting_subscription = self.portal\ + .hosting_subscription_module.template_hosting_subscription\ + .Base_createCloneDocument(batch_mode=1) + hosting_subscription.edit( + title=self.request_kw['software_title'], + reference="TESTHS-%s" % new_id, + url_string=software_url, + source_reference=self.request_kw['software_type'], + text_content=self.request_kw['instance_xml'], + sla_xml=self.request_kw['sla_xml'], + root_slave=self.request_kw['shared'], + destination_section=person.getRelativeUrl() + ) + hosting_subscription.validate() + self.portal.portal_workflow._jumpToStateFor(hosting_subscription, 'start_requested') + + return hosting_subscription + + def _makeSoftwareInstance(self, hosting_subscription, software_url): + + software_instance = self.portal.software_instance_module\ + .template_software_instance.Base_createCloneDocument(batch_mode=1) + software_instance.edit( + title=self.request_kw['software_title'], + reference="TESTSI-%s" % self.generateNewId(), + url_string=software_url, + source_reference=self.request_kw['software_type'], + text_content=self.request_kw['instance_xml'], + sla_xml=self.request_kw['sla_xml'], + specialise=hosting_subscription.getRelativeUrl() + ) + hosting_subscription.edit( + predecessor=software_instance.getRelativeUrl() + ) + self.portal.portal_workflow._jumpToStateFor(software_instance, 'start_requested') + software_instance.validate() + + return software_instance + + class TestSoftwareReleaseListFromSoftwareProduct(testSlapOSMixin): def afterSetUp(self): super(TestSoftwareReleaseListFromSoftwareProduct, self).afterSetUp() @@ -13,8 +135,8 @@ class TestSoftwareReleaseListFromSoftwareProduct(testSlapOSMixin): transaction.abort() def generateNewId(self): - return self.getPortalObject().portal_ids.generateNewId( - id_group=('slapos_core_test')) + return str(self.getPortalObject().portal_ids.generateNewId( + id_group=('slapos_core_test'))) def test_getSortedSoftwareReleaseListFromSoftwareProduct(self): new_id = self.generateNewId() @@ -27,13 +149,13 @@ class TestSoftwareReleaseListFromSoftwareProduct(testSlapOSMixin): software_release1 = self._makeSoftwareRelease(new_id) software_release1.edit( aggregate_value=software_product.getRelativeUrl(), - url_string='http://example.org/1.cfg' + url_string='http://example.org/1-%s.cfg' % new_id ) software_release1.publish() software_release2 = self._makeSoftwareRelease(self.generateNewId()) software_release2.edit( aggregate_value=software_product.getRelativeUrl(), - url_string='http://example.org/2.cfg' + url_string='http://example.org/2-%s.cfg' % new_id ) software_release2.publish() # 1 released software release, should not appear @@ -41,14 +163,14 @@ class TestSoftwareReleaseListFromSoftwareProduct(testSlapOSMixin): self.assertTrue(software_release3.getValidationState() == 'released') software_release3.edit( aggregate_value=software_product.getRelativeUrl(), - url_string='http://example.org/3.cfg' + url_string='http://example.org/3-%s.cfg' % new_id ) self.tic() release_list = software_product.SoftwareProduct_getSortedSoftwareReleaseList( software_product.getReference()) self.assertEquals([release.getUrlString() for release in release_list], - ['http://example.org/2.cfg', 'http://example.org/1.cfg']) + ['http://example.org/2-%s.cfg' % new_id, 'http://example.org/1-%s.cfg' % new_id]) def test_getSortedSoftwareReleaseListFromSoftwareProduct_Changed(self): @@ -63,18 +185,101 @@ class TestSoftwareReleaseListFromSoftwareProduct(testSlapOSMixin): software_release2.publish() software_release2.edit( aggregate_value=software_product.getRelativeUrl(), - url_string='http://example.org/2.cfg' + url_string='http://example.org/2-%s.cfg' % new_id ) # Newest software release software_release1 = self._makeSoftwareRelease(new_id) software_release1.publish() software_release1.edit( aggregate_value=software_product.getRelativeUrl(), - url_string='http://example.org/1.cfg' + url_string='http://example.org/1-%s.cfg' % new_id ) self.tic() release_list = software_product.SoftwareProduct_getSortedSoftwareReleaseList( software_product.getReference()) self.assertEquals([release.getUrlString() for release in release_list], - ['http://example.org/1.cfg', 'http://example.org/2.cfg']) + ['http://example.org/1-%s.cfg' % new_id, 'http://example.org/2-%s.cfg' % new_id]) + release_list = software_product.SoftwareProduct_getSortedSoftwareReleaseList( + software_release_url='http://example.org/1-%s.cfg' % new_id) + self.assertEquals([release.getUrlString() for release in release_list], + ['http://example.org/1-%s.cfg' % new_id, 'http://example.org/2-%s.cfg' % new_id]) + + +class TestSlapOSUpgradeHostingSubscription(BaseTestSlapOSMixin): + def afterSetUp(self): + super(TestSlapOSUpgradeHostingSubscription, self).afterSetUp() + self.new_id = self.generateNewId() + + def beforeTearDown(self): + transaction.abort() + + def generateNewId(self): + return str(self.getPortalObject().portal_ids.generateNewId( + id_group=('slapos_core_test'))) + + def _requestSoftwareRelease(self, new_id, software_product_url, software_url): + software_release = self._makeSoftwareRelease(new_id) + software_release.edit( + aggregate_value=software_product_url, + url_string=software_url + ) + software_release.publish() + return software_release + + def test_HostingSubscription_getNewerSofwareRelease(self): + computer = self._makeComputer(self.new_id) + software_product = self._makeSoftwareProduct(self.new_id) + oldest_software_url = 'http://example.org/oldest-%s.cfg' % self.new_id + newest_software_url = 'http://example.org/newest-%s.cfg' % self.new_id + + self._requestSoftwareRelease(self.new_id, + software_product.getRelativeUrl(), + oldest_software_url) + self._requestSoftwareRelease(self.generateNewId(), + software_product.getRelativeUrl(), + newest_software_url) + self._makeSoftwareInstallation(self.new_id, computer, oldest_software_url) + + hosting_subscription = self._makeHostingSubscription(self.new_id, oldest_software_url) + self.tic() + self.assertEqual(hosting_subscription.HostingSubscription_getNewerSofwareRelease(), + "") + + self._makeSoftwareInstance(hosting_subscription, oldest_software_url) + self.tic() + self.assertEqual(hosting_subscription.HostingSubscription_getNewerSofwareRelease(), + newest_software_url) + + def test_HostingSubscription_isUpgradable(self): + computer = self._makeComputer(self.new_id) + self._makeComputerPartitions(computer) + software_product = self._makeSoftwareProduct(self.new_id) + oldest_software_url = 'http://example.org/oldest-%s.cfg' % self.new_id + newest_software_url = 'http://example.org/newest-%s.cfg' % self.new_id + self._requestSoftwareRelease(self.new_id, + software_product.getRelativeUrl(), + oldest_software_url) + self._makeSoftwareInstallation(self.new_id, computer, oldest_software_url) + hosting_subscription = self._makeHostingSubscription(self.new_id, oldest_software_url) + self.tic() + self.assertEqual(hosting_subscription.HostingSubscription_isUpgradable(), + False) + + self._makeSoftwareInstance(hosting_subscription, oldest_software_url) + self._markComputerPartitionBusy(computer, hosting_subscription.getPredecessorValue()) + self._requestSoftwareRelease(self.generateNewId(), + software_product.getRelativeUrl(), + newest_software_url) + self.tic() + self.assertEqual(hosting_subscription.HostingSubscription_isUpgradable(), + False) + self._makeSoftwareInstallation(self.generateNewId(), computer, newest_software_url) + self.tic() + self.assertEqual(hosting_subscription.HostingSubscription_isUpgradable(), + True) + + self.portal.portal_workflow._jumpToStateFor(hosting_subscription, 'destroy_requested') + self.assertEqual(hosting_subscription.HostingSubscription_isUpgradable(), + False) + \ No newline at end of file diff --git a/master/bt5/slapos_cloud/bt/revision b/master/bt5/slapos_cloud/bt/revision index 75af06ed6776f1c1e133f3684b1a1f03c44ec429..272a4581e99a8e4b8a0bfd3429e493850a30788b 100644 --- a/master/bt5/slapos_cloud/bt/revision +++ b/master/bt5/slapos_cloud/bt/revision @@ -1 +1 @@ -315 \ No newline at end of file +316 \ No newline at end of file