Commit b97349ec authored by Łukasz Nowak's avatar Łukasz Nowak

Merge branch 'master' into external_buildout

parents b376d945 1b1b35c6
0.3 (unreleased)
0.4 (unreleased)
================
* Do not use buildout internally, but rather call bootstrap command of any
provided buildout binary. [Łukasz Nowak]
0.3 (2011-06-14)
================
* slap: Implement SLA by filter_kw in OpenOrder.request. [Łukasz Nowak]
* slap: Timeout network operations. [Łukasz Nowak]
* slapformat: Make slapsoft and slapuser* system users. [Kazuhiko Shiozaki]
* slapgrid: Add more tolerance with supervisord. [Łukasz Nowak]
0.2 (2011-06-01)
================
......
......@@ -55,27 +55,6 @@ from AccessControl import getSecurityManager\n
from Products.ERP5Type.Log import log\n
\n
portal = context.getPortalObject()\n
bt = portal.portal_templates.getInstalledBusinessTemplate("erp5_demo_maxma_sample")\n
isTransitionPossible = portal.portal_workflow.isTransitionPossible\n
\n
for obj in portal.portal_catalog(path=["%%/%s" % i.replace("**", "%") for i in bt.getTemplatePathList()]):\n
obj.activate().updateLocalRolesOnSecurityGroups()\n
\n
for document in portal.portal_catalog(portal_type=bt.getTemplatePortalTypeRoleList()):\n
document.updateLocalRolesOnSecurityGroups()\n
\n
conversion_server_hostname = portal.portal_preferences.getPreferredOoodocServerAddress()\n
conversion_server_port = portal.portal_preferences.getPreferredOoodocServerPortNumber()\n
for preference_id in ["default_configurator_preference", "default_configurator_system_preference"]:\n
preference = getattr(portal.portal_preferences, preference_id)\n
if preference.getPortalType() == "System Preference":\n
preference.setPreferredOoodocServerPortNumber(conversion_server_port)\n
preference.setPreferredOoodocServerAddress(conversion_server_hostname)\n
\n
if isTransitionPossible(preference, "enable"):\n
preference.enable()\n
preference.updateLocalRolesOnSecurityGroups()\n
\n
for gadget in portal.portal_gadgets.objectValues():\n
if gadget.getValidationState() == \'invisible\':\n
gadget.visible()\n
......
......@@ -95,10 +95,10 @@ bt5_installation_list = ( \n
\'vifib_crm\', \n
\'vifib_forge_release\', \n
\'vifib_software_pdm\', \n
\'vifib_web\', \n
\'vifib_web\',\n
\'vifib_open_trade\', \n
\'vifib_l10n_fr\'\n
\'vifib_datas\'\n
\'vifib_l10n_fr\',\n
\'vifib_datas\',\n
\'vifib_erp5\'\n
)\n
\n
......
......@@ -29,42 +29,61 @@ from Products.ERP5Configurator.tests.ConfiguratorTestMixin import \
TestLiveConfiguratorWorkflowMixin
from Products.ERP5Type.tests.Sequence import SequenceList
class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
class TestVifibConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
"""
Configurator Mixin Class
"""
# The list of standard business templates that the configurator should force
# to install
user_reference = "demo"
standard_bt5_list = ('erp5_simulation',
'erp5_dhtml_style',
'erp5_jquery',
'erp5_jquery_ui',
'erp5_xhtml_jquery_style',
'erp5_ingestion_mysql_innodb_catalog',
'erp5_ingestion',
'erp5_web',
'erp5_dms',
'erp5_crm',
'erp5_pdm',
'erp5_trade',
'erp5_knowledge_pad',
'erp5_accounting',
'erp5_tax_resource',
'erp5_discount_resource',
'erp5_invoicing',
'erp5_configurator_standard_categories',
'erp5_trade_knowledge_pad',
'erp5_crm_knowledge_pad',
'erp5_simulation_test',
'erp5_simplified_invoicing',
'erp5_ods_style',
'erp5_odt_style',
'erp5_ooo_import',
'erp5_accounting_l10n_fr',
'erp5_l10n_fr',
'erp5_l10n_pt-BR',
'erp5_demo_maxma_rule')
standard_bt5_list = (
'erp5_simulation',
'erp5_administration',
'erp5_pdm',
'erp5_trade',
'erp5_simulation_test',
'erp5_item',
'erp5_open_trade',
'erp5_forge',
'erp5_ingestion_mysql_innodb_catalog',
'erp5_ingestion',
'erp5_crm',
'erp5_jquery',
'erp5_jquery_ui',
'erp5_knowledge_pad',
'erp5_web',
'erp5_dms',
'erp5_l10n_fr',
'erp5_content_translation',
'erp5_software_pdm',
'erp5_computer_immobilisation',
'erp5_accounting',
'erp5_accounting_l10n_fr',
'erp5_tax_resource',
'erp5_discount_resource',
'erp5_invoicing',
'erp5_ods_style',
'erp5_odt_style',
'erp5_ooo_import',
'erp5_simplified_invoicing',
'erp5_legacy_tax_system',
'erp5_commerce',
'erp5_project',
'erp5_credential',
'erp5_km',
'erp5_web_download_theme',
'vifib_mysql_innodb_catalog',
'vifib_core',
'vifib_base',
'vifib_slap',
'vifib_crm',
'vifib_forge_release',
'vifib_software_pdm',
'vifib_web',
'vifib_open_trade',
'vifib_l10n_fr',
'vifib_datas',
'vifib_erp5')
def getBusinessTemplateList(self):
return ('erp5_core_proxy_field_legacy',
......@@ -72,7 +91,7 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
'erp5_base',
'erp5_workflow',
'erp5_configurator',
'erp5_configurator_maxma_demo',)
'erp5_configurator_vifib',)
def stepCreateBusinessConfiguration(self, sequence=None,\
sequence_list=None, **kw):
......@@ -80,7 +99,7 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
module = self.portal.business_configuration_module
business_configuration = module.newContent(
portal_type="Business Configuration",
title='Test Configurator Maxma Demo Workflow')
title='Test Configurator Vifib Workflow')
next_dict = {}
sequence.edit(business_configuration=business_configuration,
next_dict=next_dict)
......@@ -94,44 +113,13 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
self.assertEquals('show', response_dict['command'])
self.assertEquals('Install', response_dict['next'])
def stepSetMaxmaDemoWorkflow(self, sequence=None, sequence_list=None, **kw):
def stepSetVifibWorkflow(self, sequence=None, sequence_list=None, **kw):
""" Set Consulting Workflow into Business Configuration """
business_configuration = sequence.get("business_configuration")
self.setBusinessConfigurationWorkflow(business_configuration,
"workflow_module/maxma_demo_configuration_workflow")
"workflow_module/vifib_configuration_workflow")
def stepViewCreatedPersons(self, sequence=None, sequence_list=None, **kw):
self.login(user_name='test_configurator_user')
person_list = self.portal.person_module.searchFolder()
self.assertNotEquals(0, len(person_list))
for entity in person_list:
for username in self.all_username_list:
self.failUnlessUserCanAccessDocument(username, entity)
self.failUnlessUserCanViewDocument(username, entity)
def stepViewCreatedOrganisations(self, sequence=None, sequence_list=None, **kw):
self.login(user_name='test_configurator_user')
organisation_list = self.portal.organisation_module.searchFolder()
self.assertNotEquals(0, len(organisation_list))
for entity in organisation_list:
for username in self.all_username_list:
self.failUnlessUserCanAccessDocument(username, entity)
self.failUnlessUserCanViewDocument(username, entity)
def stepViewCreatedAssignemnts(self, sequence=None, sequence_list=None, **kw):
self.login(user_name='test_configurator_user')
person_list = self.portal_person_module.searchFolder()
self.assertNotEquals(0, len(person_list))
for person in person_list:
for assignment in person.contentValues(portal_type='Assignment'):
for username in self.all_username_list:
self.failUnlessUserCanAccessDocument(username, assignment)
self.failUnlessUserCanViewDocument(username, assignment)
def stepCheckMaxmaDemoSampleObjectList(self, sequence=None, sequence_list=None, **kw):
""" Check if objects are placed into the appropriate state """
......@@ -143,22 +131,11 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
gadget.getValidationState()))
gadget.Base_checkConsistency()
# Check if demo user is working.
user = self.portal.portal_catalog.getResultValue(portal_type="Person",
reference=self.user_reference)
self.assertNotEquals(user.Person_getAvailableAssignmentValueList(), [])
self.assertEquals(user.getTitle(), "Jack Vale")
self.assertEquals(user.getValidationState(), "validated")
self.assertEquals(user.getSubordination(),
'organisation_module/myorganisation')
self.assertEquals(user.getSubordinationTitle(), "Maxma Co")
### STEPS
DEFAULT_SEQUENCE_LIST = """
stepCreateBusinessConfiguration
stepTic
stepSetMaxmaDemoWorkflow
stepSetVifibWorkflow
stepTic
stepConfiguratorNext
stepTic
......@@ -171,61 +148,9 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
stepStartConfigurationInstallation
stepTic
stepCheckInstanceIsConfigured%(country)s
stepCheckMaxmaDemoSampleObjectList
stepTic
stepViewAddGadget
stepViewEventModule
stepAddEvent
stepSentEventWorkflow
stepViewAccountModule
stepAddAccountModule
stepViewAccount
stepCopyPasteAccount
stepViewEntityModules
stepAddEntityModules
stepCopyAndPastePerson
stepCopyAndPasteOrganisation
stepEntityWorkflow
stepViewCreatedPersons
stepViewCreatedOrganisations
stepViewCreatedAssignemnts
stepAddAccoutingPeriod
stepValidatedAccountingPeriods
stepViewBankAccount
stepViewCreditCard
stepValidateAndModifyBankAccount
stepValidateAndModifyCreditCard
stepAddPaymentNodeInPerson
stepAddPaymentNodeInOrganisation
stepCopyAndPasteBankAccountInPerson
stepCopyAndPasteBankAccountInOrganisation
stepViewAccountingTransactionModule
stepAddAccountingTransactionModule
stepCopyAndPasteAccountingTransactions
stepTic
stepAccountingTransaction
stepTic
stepSaleInvoiceTransaction
stepTic
stepPurchaseInvoiceTransaction
stepTic
stepPaymentTransaction
stepTic
stepBalanceTransaction
stepTic
stepAccountingTransaction_getCausalityGroupedAccountingTransactionList
stepAddAssignments
stepAssignmentTI
stepEditAssignments
stepViewAcessAddPurchaseTradeCondition
stepViewAccessAddSaleTradeCondition
stepViewAccessAddSaleOrder
stepViewAccessAddSalePackingList
stepViewAccessPurchaseOrder
stepPurchasePackingList
"""
def test_maxma_demo_workflow(self):
def test_vifib_workflow(self):
""" Test the consulting workflow configuration"""
self.all_username_list = ["demo"]
self.accountant_username_list = self.all_username_list
......@@ -241,5 +166,5 @@ class TestMaxmaDemoConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
import unittest
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestMaxmaDemoConfiguratorWorkflow))
suite.addTest(unittest.makeSuite(TestVifibConfiguratorWorkflow))
return suite
8
\ No newline at end of file
11
\ No newline at end of file
testMaxmaDemoConfigurationWorkflow
\ No newline at end of file
testVifibConfigurationWorkflow
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>get_certificate</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Get Certificate</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_getCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>revoke_certificate</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>11.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Revoke Certificate</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Person_revokeCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
from AccessControl import ClassSecurityInfo, Unauthorized, getSecurityManager
from Products.ERP5.Document.Person import Person as ERP5Person
class Person(ERP5Person):
security = ClassSecurityInfo()
security.declarePublic('getCertificate')
def _checkCertificateRequest(self):
try:
self.checkUserCanChangePassword()
except Unauthorized:
# in ERP5 user has no SetOwnPassword permission on Person document
# referring himself, so implement "security" by checking that currently
# logged in user is trying to get/revoke his own certificate
reference = self.getReference()
if not reference:
raise
if getSecurityManager().getUser().getId() != reference:
raise
def _getCertificate(self):
return self.getPortalObject().portal_certificate_authority\
.getNewCertificate(self.getReference())
def _revokeCertificate(self):
return self.getPortalObject().portal_certificate_authority\
.revokeCertificateByCommonName(self.getReference())
def getCertificate(self):
"""Returns new SSL certificate"""
self._checkCertificateRequest()
return self._getCertificate()
security.declarePublic('revokeCertificate')
def revokeCertificate(self):
"""Revokes existing certificate"""
self._checkCertificateRequest()
self._revokeCertificate()
......@@ -28,6 +28,7 @@
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5.Document.Item import Item
from lxml import etree
class SoftwareInstance(Item):
"""
......@@ -42,3 +43,20 @@ class SoftwareInstance(Item):
security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declareProtected(Permissions.AccessContentsInformation,
'getSlaXmlAsDict')
def getSlaXmlAsDict(self):
"""Returns SLA XML as python dictionary"""
result_dict = {}
xml = self.getSlaXml()
if xml is not None and xml != '':
tree = etree.fromstring(xml.encode('utf-8'))
for element in tree.findall('parameter'):
key = element.get('id')
value = result_dict.get(key, None)
if value is not None:
value = value + ' ' + element.text
else:
value = element.text
result_dict[key] = value
return result_dict
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/text</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>XML Service Level Agreement parameters</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>sla_xml_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>certificate = context.getCertificate()\n
request = context.REQUEST\n
request.set(\'your_certificate\', certificate[\'certificate\'])\n
request.set(\'your_key\', certificate[\'key\'])\n
return context.Person_getCertificateForm()\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>dialog_id=None, form_id=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5Form" module="Products.ERP5Form.Form"/>
</pickle>
<pickle>
<dictionary>
<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/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>your_certificate</string>
<string>your_key</string>
</list>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>your_tip</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_getCertificateForm</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Person_getCertificateForm</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Certificate Request</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>context.revokeCertificate()\n
return context.Base_redirect(form_id, keep_items = {\'portal_status_message\' : \'Certificate revoked.\'}, **kw)\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>dialog_id=None, form_id=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_revokeCertificate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -86,6 +86,7 @@
<string>my_ssl_key</string>
<string>my_ssl_certificate</string>
<string>my_description</string>
<string>my_sla_xml</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_sla_xml</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_area_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Service Level Agreement XML</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -68,20 +68,22 @@ sale_packing_list = sale_packing_list_module.newContent(\n
source=instance_setup_sale_packing_list.getSource(),\n
source_section=instance_setup_sale_packing_list.getSourceSection(),\n
price_currency=instance_setup_sale_packing_list.getPriceCurrency(),\n
start_date=DateTime())\n
start_date=DateTime(),\n
activate_kw={\'tag\': tag})\n
\n
instance_hosting_sale_packing_list_line = sale_packing_list.newContent(\n
portal_type=\'Sale Packing List Line\',\n
resource=service_relative_url,\n
quantity=instance_setup_sale_packing_list_line.getQuantity(),\n
aggregate_list=instance_setup_sale_packing_list_line.getAggregateList(),\n
activate_kw={\'tag\': tag}\n
)\n
return sale_packing_list\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change, service_relative_url</string> </value>
<value> <string>state_change, service_relative_url, tag=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -61,7 +61,7 @@ hosting_subscription_uid = state_change.kwargs[\'hosting_subscription_uid\']\n
shared = state_change.kwargs[\'shared\']\n
software_type = state_change.kwargs["software_type"]\n
tag = state_change.kwargs[\'tag\']\n
filter_kw = state_change.kwargs["filter_kw"]\n
filter_kw = software_instance.getSlaXmlAsDict()\n
\n
# Assertion: No packing list line should be related to this software instance\n
packing_list_line = software_instance.getAggregateRelatedValue(portal_type=\'Sale Packing List Line\')\n
......
......@@ -50,12 +50,22 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>software_instance = state_change[\'object\']\n
<value> <string encoding="cdata"><![CDATA[
software_instance = state_change[\'object\']\n
tag = "%s_destroyInProgress" % software_instance.getUid()\n
portal = context.getPortalObject()\n
if (portal.portal_activities.countMessageWithTag(tag) > 0):\n
raise ValueError("Software Instance is currently being destroyed.")\n
# lock software instance in transaction\n
software_instance.serialize()\n
service_relative_url = software_instance.portal_preferences.\\\n
getPreferredInstanceCleanupResource()\n
sale_packing_list = context.SoftwareInstance_createSalePackingList(state_change, service_relative_url)\n
sale_packing_list.confirm()\n
</string> </value>
sale_packing_list = context.SoftwareInstance_createSalePackingList(state_change, service_relative_url, tag=tag)\n
sale_packing_list.confirm(activate_kw={\'tag\':tag})\n
]]></string> </value>
</item>
<item>
<key> <string>_params</string> </key>
......
......@@ -61,7 +61,7 @@ requested_partition_reference = kwargs["partition_reference"]\n
shared = kwargs["shared"]\n
software_type = kwargs["software_type"]\n
instance_xml = kwargs["instance_xml"]\n
filter_kw = kwargs["filter_kw"]\n
sla_xml = kwargs["sla_xml"]\n
\n
# Get root software instance\n
predecessor_software_instance = software_instance\n
......@@ -97,6 +97,7 @@ if (request_software_instance is None):\n
title=requested_partition_reference,\n
source_reference=software_type,\n
text_content=instance_xml,\n
sla_xml=sla_xml,\n
activate_kw={\'tag\': tag},\n
**portal.Base_getNewSoftwareInstanceCoordinate()\n
)\n
......@@ -108,13 +109,13 @@ if (request_software_instance is None):\n
hosting_subscription_uid=hosting_subscription_uid,\n
shared=shared,\n
software_type=software_type,\n
tag=tag,\n
filter_kw=filter_kw)\n
tag=tag)\n
else:\n
# Update existing software instance\n
# Sale Packing List interaction has to be requested automatically with an interaction workflow\n
request_software_instance.edit(\n
text_content=instance_xml,\n
sla_xml=sla_xml,\n
activate_kw={\'tag\': tag},\n
)\n
# Update the predecessor category of the original caller\n
......
......@@ -50,7 +50,9 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>software_instance = state_change[\'object\']\n
<value> <string encoding="cdata"><![CDATA[
software_instance = state_change[\'object\']\n
service_relative_url = software_instance.portal_preferences.\\\n
getPreferredInstanceHostingResource()\n
need_to_create_packing_list = False\n
......@@ -63,10 +65,18 @@ else:\n
if sale_packing_list_line.getSimulationState() == \'delivered\':\n
need_to_create_packing_list = True\n
\n
portal = context.getPortalObject()\n
if need_to_create_packing_list:\n
sale_packing_list = context.SoftwareInstance_createSalePackingList(state_change, service_relative_url)\n
sale_packing_list.confirm()\n
</string> </value>
tag = "%s_startInProgress" % software_instance.getUid()\n
if (portal.portal_activities.countMessageWithTag(tag) > 0):\n
raise ValueError("Software Instance is currently being started.")\n
# lock software instance in transaction\n
software_instance.serialize()\n
sale_packing_list = context.SoftwareInstance_createSalePackingList(state_change, service_relative_url, tag)\n
sale_packing_list.confirm(activate_kw={\'tag\':tag})\n
]]></string> </value>
</item>
<item>
<key> <string>_params</string> </key>
......
230
\ No newline at end of file
243
\ No newline at end of file
Person | get_certificate
Person | revoke_certificate
Software Instance Module | view
Software Instance | jump_to_software_instance
Software Instance | view
\ No newline at end of file
SoftwareInstance
\ No newline at end of file
SoftwareInstance
Person
\ No newline at end of file
......@@ -41,109 +41,46 @@ class TestVifibCredential(testVifibMixin):
result_list.append("vifib_credential")
return result_list
def createCredentialRequest(self,
first_name="Gabriel",
last_name="Monnerat",
reference="gabriel",
password="123",
default_email_text="gabriel@test.com"):
def stepSetCredentialRequestAutomaticApprovalPreferences(self):
self.setSystemPreference()
default_system_preference = self.portal.portal_preferences.restrictedTraverse(self.getDefaultSitePreferenceId())
self.login("ERP5TypeTestCase")
default_system_preference.edit(preferred_credential_request_automatic_approval=True)
self.logout()
def stepValidateNotificationMessage(self):
module = self.portal.notification_message_module
reference = "crendential_request-confirmation-without-password"
search_result = module.searchFolder(reference=reference)
[notification.getObject().validate() for notification in search_result]
def testBase_getDefaultAssignmentArgumentDict(self):
self.stepValidateNotificationMessage()
self.stepSetCredentialRequestAutomaticApprovalPreferences()
self.logout()
self.portal.ERP5Site_registerCredentialRequest(first_name=first_name,
last_name=last_name,
reference=reference,
password=password,
portal_catalog = self.portal.portal_catalog
self.portal.ERP5Site_registerCredentialRequest(first_name="Vifib",
last_name="Test",
reference="vifib_test",
password="vifib",
career_subordination_title="",
default_email_text=default_email_text,
default_email_text="vifib@vifib.com",
default_telephone_text="223344",
default_address_street_address="Test Street",
default_address_city="Campos",
default_address_city="My Street",
default_address_zip_code="28024030")
self.login("ERP5TypeTestCase")
self.stepTic()
def createSystemPreference(self):
""" """
portal_preferences = self.getPreferenceTool()
preference = portal_preferences.newContent(portal_type='System Preference',
title='Default Site Preference',
id='test_site_preference')
self.stepTic()
return preference
def afterSetUp(self):
""" """
portal_preferences = self.getPreferenceTool()
preference = getattr(portal_preferences, 'test_site_preference', None)
if preference is None:
preference = self.createSystemPreference()
if preference.getPreferenceState() == 'disabled':
preference.enable()
preference.edit(preferred_credential_request_automatic_approval=True,
preferred_credential_recovery_automatic_approval=True,
preferred_organisation_credential_update_automatic_approval=True,
preferred_person_credential_update_automatic_approval=True)
self.portal.email_from_address = 'site@example.invalid'
oldMailHost = getattr(self.portal, 'MailHost', None)
if oldMailHost is not None:
self.portal.manage_delObjects(['MailHost'])
self.portal._setObject('MailHost', DummyMailHost('MailHost'))
self.stepTic()
def testMailMessagePosted(self):
""" Test if the Mail Message was posted correctly """
self.createCredentialRequest(reference="vifibtest")
portal_catalog = self.portal.portal_catalog
credential_request = portal_catalog.getResultValue(portal_type="Credential Request",
reference="vifibtest")
mail_message = portal_catalog.getResultValue(portal_type="Mail Message",
follow_up=credential_request)
self.assertEquals(mail_message.getSimulationState(), "started")
self.assertTrue("key=%s" % mail_message.getReference() in mail_message.getTextContent())
def test_MailFromMailMessageEvent(self):
""" """
self.createCredentialRequest(first_name="Vifib",
last_name="Test",
reference="vifibtest")
portal_catalog = self.portal.portal_catalog
credential_request = portal_catalog.getResultValue(portal_type="Credential Request",
reference="vifibtest",
first_name="Vifib",
last_name="Test")
mail_message = portal_catalog.getResultValue(portal_type="Mail Message",
follow_up=credential_request)
last_message = self.portal.MailHost._last_message
self.assertNotEquals((), last_message)
mfrom, mto, message_text = last_message
self.assertEquals(mfrom, 'Portal Administrator <site@example.invalid>')
self.assertEquals(['Vifib Test <gabriel@test.com>'], mto)
self.assertNotEquals(re.search("Subject\:.*Welcome_to_Vifib", message_text), None)
self.assertNotEquals(re.search("Hello\ Vifib\ Test\,", message_text), None)
self.assertNotEquals(re.search("key\=..%s" % mail_message.getReference(), message_text), None)
def testERP5Site_activeLogin(self):
""" Test if the script WebSection_activeLogin will create one user
correctly """
self.createCredentialRequest()
portal_catalog = self.portal.portal_catalog
credential_request = portal_catalog.getResultValue(portal_type="Credential Request",
reference="gabriel")
credential_request = portal_catalog.getResultValue(portal_type="Credential Request",
reference="vifib_test")
mail_message = portal_catalog.getResultValue(portal_type="Mail Message",
follow_up=credential_request)
self.logout()
self.portal.ERP5Site_activeLogin(mail_message.getReference())
self.login("ERP5TypeTestCase")
self.stepTic()
person = portal_catalog.getResultValue(reference="gabriel", portal_type="Person")
self.assertEquals(person.getValidationState(), "validated")
def testERP5Site_registerCredentialRequest(self):
""" Test if the script ERP5Site_registerCredentialRequest will create one
Credential Request correctly """
self.createCredentialRequest()
portal_catalog = self.portal.portal_catalog
credential_request = portal_catalog.getResultValue(portal_type="Credential Request",
reference="gabriel")
self.assertEquals(credential_request.getFirstName(), "Gabriel")
self.assertEquals(credential_request.getDefaultEmailText(), "gabriel@test.com")
person = portal_catalog.getResultValue(reference="vifib_test", portal_type="Person")
assignment_list = person.objectValues(portal_type="Assignment")
assignment = assignment_list[0]
self.assertEquals(assignment.getFunction(), "customer")
self.assertEquals(assignment.getRole(), "client")
27
\ No newline at end of file
28
\ No newline at end of file
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TradeModelPath" module="Products.ERP5Type.Document.TradeModelPath"/>
<global name="Trade Model Path" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Computer" module="Products.ERP5Type.Document.Computer"/>
<global name="Computer" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ComputerPartition" module="Products.ERP5Type.Document.ComputerPartition"/>
<global name="Computer Partition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Supply" module="Products.ERP5Type.Document.Supply"/>
<global name="Open Sale Order" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SupplyLine" module="Products.ERP5Type.Document.SupplyLine"/>
<global name="Open Sale Order Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Organisation" module="Products.ERP5Type.Document.Organisation"/>
<global name="Organisation" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Organisation" module="Products.ERP5Type.Document.Organisation"/>
<global name="Organisation" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="AccountingTransaction" module="Products.ERP5Type.Document.AccountingTransaction"/>
<global name="Accounting Period" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="GeographicAddress" module="Products.ERP5Type.Document.GeographicAddress"/>
<global name="Address" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Url" module="Products.ERP5Type.Document.Url"/>
<global name="Email" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Telephone" module="Products.ERP5Type.Document.Telephone"/>
<global name="Telephone" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SubscriptionItemRootSimulationRule" module="Products.ERP5Type.Document.SubscriptionItemRootSimulationRule"/>
<global name="Subscription Item Root Simulation Rule" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="FloatEquivalenceTester" module="Products.ERP5Type.Document.FloatEquivalenceTester"/>
<global name="Float Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="NetConvertedQuantityEquivalenceTester" module="Products.ERP5Type.Document.NetConvertedQuantityEquivalenceTester"/>
<global name="Net Converted Quantity Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="CategoryMembershipEquivalenceTester" module="Products.ERP5Type.Document.CategoryMembershipEquivalenceTester"/>
<global name="Category Membership Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="DateTimeEquivalenceTester" module="Products.ERP5Type.Document.DateTimeEquivalenceTester"/>
<global name="DateTime Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="DateTimeEquivalenceTester" module="Products.ERP5Type.Document.DateTimeEquivalenceTester"/>
<global name="DateTime Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariationEquivalenceTester" module="Products.ERP5Type.Document.VariationEquivalenceTester"/>
<global name="Variation Divergence Tester" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PackingList" module="Products.ERP5Type.Document.PackingList"/>
<global name="Purchase Packing List" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="DeliveryLine" module="Products.ERP5Type.Document.DeliveryLine"/>
<global name="Purchase Packing List Line" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TradeCondition" module="Products.ERP5Type.Document.TradeCondition"/>
<global name="Sale Trade Condition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TradeCondition" module="Products.ERP5Type.Document.TradeCondition"/>
<global name="Sale Trade Condition" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SoftwareProduct" module="Products.ERP5Type.Document.SoftwareProduct"/>
<global name="Software Product" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -10,6 +10,7 @@
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
......@@ -44,6 +45,7 @@
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
......@@ -84,7 +86,127 @@
<key> <string>title</string> </key>
<value> <string>test_software_product</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>commerce_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_action</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global id="3.1" name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1307981928.46</float>
<string>GMT+2</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>draft</string> </value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass> <reference id="3.1"/> </klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1307981928.46</float>
<string>GMT+2</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
......@@ -66,13 +66,16 @@ for order_line in order.getMovementList():\n
computer_partition = None\n
\n
# XXX Duplicated from request method...\n
\n
query_kw = {\n
\'software_release_url\': software_release.getUrlString(),\n
\'portal_type\': \'Computer Partition\',\n
# \'relative_url\': \'computer_module/20110126-E2AE/%\',\n
\'free_for_request\': 1,\n
}\n
# support SLA\n
filter_kw = software_instance.getSlaXmlAsDict()\n
if "computer_guid" in filter_kw:\n
query_kw["parent_reference"] = filter_kw["computer_guid"]\n
\n
result_count = software_instance.portal_catalog.countResults(**query_kw)[0][0]\n
offset = max(0, result_count-1)\n
......
85
\ No newline at end of file
88
\ No newline at end of file
software_release_module/test_software_release
\ No newline at end of file
software_release_module/test_software_release
software_product_module/test_software_product
\ No newline at end of file
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Alarm" module="Products.ERP5Type.Document.Alarm"/>
<global name="Alarm" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -59,7 +59,7 @@
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>Folder</string> </value>
<value> <string>Products.Vifib.Tool.SlapTool.SlapTool</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="SlapTool" module="Products.Vifib.Tool.SlapTool"/>
<global name="Slap Tool" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......
......@@ -59,8 +59,9 @@ portal = person.getPortalObject()\n
kwargs = state_change.kwargs\n
software_release_url_string = state_change.kwargs[\'software_release\']\n
software_title = kwargs["software_title"]\n
software_type = "RootSoftwareInstance"\n
software_type = kwargs.get("software_type") or "RootSoftwareInstance"\n
instance_xml = kwargs["instance_xml"]\n
sla_xml = kwargs.get("sla_xml") or ""\n
\n
sale_order_portal_type = "Sale Order"\n
sale_order_line_portal_type = "Sale Order Line"\n
......@@ -96,6 +97,7 @@ if (request_software_instance is None):\n
source_reference=software_type,\n
title=software_title,\n
text_content=instance_xml,\n
sla_xml=sla_xml,\n
activate_kw={\'tag\': tag},\n
**portal.Base_getNewSoftwareInstanceCoordinate()\n
)\n
......@@ -168,6 +170,7 @@ else:\n
# Update existing software instance\n
request_software_instance.edit(\n
text_content=instance_xml,\n
sla_xml=sla_xml,\n
activate_kw={\'tag\': tag},\n
)\n
......
372
\ No newline at end of file
378
\ No newline at end of file
......@@ -180,7 +180,8 @@ class CertificateAuthorityTool(BaseTool):
security.declareProtected(Permissions.AccessContentsInformation, 'getNewCertificate')
def getNewCertificate(self, common_name):
"""Returns certificate for passed common name, as dictionary of {key, certificate, id, common_name}"""
# No docstring in order to make this method non publishable
# Returns certificate for passed common name, as dictionary of {key, certificate, id, common_name}
self._checkCertificateAuthority()
self._lockCertificateAuthority()
try:
......@@ -215,7 +216,8 @@ class CertificateAuthorityTool(BaseTool):
security.declareProtected(Permissions.AccessContentsInformation, 'revokeCertificate')
def revokeCertificate(self, serial):
"""Revokes certificate with serial, returns dictionary {crl}"""
# No docstring in order to make this method non publishable
# Revokes certificate with serial, returns dictionary {crl}
self._checkCertificateAuthority()
self._lockCertificateAuthority()
try:
......@@ -247,4 +249,19 @@ class CertificateAuthorityTool(BaseTool):
finally:
self._unlockCertificateAuthority()
def _getValidSerial(self, common_name):
index = open(self.index).read().splitlines()
valid_line_list = [q for q in index if q.startswith('V') and
('CN=%s' % common_name in q)]
if len(valid_line_list) != 1:
raise ValueError('No certificate for %r' % common_name)
return valid_line_list[0].split('\t')[3]
security.declareProtected(Permissions.AccessContentsInformation,
'revokeCertificate')
def revokeCertificateByCommonName(self, common_name):
self._checkCertificateAuthority()
serial = self._getValidSerial(common_name)
self.revokeCertificate(serial)
InitializeClass(CertificateAuthorityTool)
This diff is collapsed.
......@@ -47,6 +47,7 @@ from Products.ERP5Security.ERP5GroupManager import ConsistencyError, NO_CACHE_MO
from Products.ERP5Type.ERP5Type \
import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
from Products.ERP5Type.Cache import CachingMethod
from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
#Form for new plugin in ZMI
manage_addVifibMachineAuthenticationPluginForm = PageTemplateFile(
......@@ -70,11 +71,14 @@ def addVifibMachineAuthenticationPlugin(dispatcher, id, title=None, REQUEST=None
def getUserByLogin(portal, login):
if isinstance(login, basestring):
login = login,
result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference',
portal_type=["Computer", "Software Instance"],
machine_query = Query(portal_type=["Computer", "Software Instance"],
validation_state="validated",
reference=dict(query=login, key='ExactMatch'))
person_query = Query(portal_type=["Person"],
reference=dict(query=login, key='ExactMatch'))
result = portal.portal_catalog.unrestrictedSearchResults(
query=ComplexQuery(machine_query, person_query, operator="OR"),
select_expression='reference')
# XXX: Here, we filter catalog result list ALTHOUGH we did pass
# parameters to unrestrictedSearchResults to restrict result set.
# This is done because the following values can match person with
......@@ -92,7 +96,6 @@ def getUserByLogin(portal, login):
# by default (feature).
return [x.getObject() for x in result if x['reference'] in login]
class VifibMachineAuthenticationPlugin(BasePlugin):
"""
Plugin to authenicate as machines.
......
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
import glob
import os
version = '0.3-dev'
version = '0.4-dev'
name = 'slapos.core'
long_description = open("README.txt").read() + "\n" + \
open("CHANGES.txt").read() + "\n"
......@@ -33,7 +33,7 @@ setup(name=name,
install_requires=[
'Flask', # used by proxy
'lxml', # needed to play with XML trees
'netaddr', # to play safely with IPv6 prefixes
'netaddr>=0.7.5', # to play safely with IPv6 prefixes
'netifaces', # to fetch information about network devices
'setuptools', # namespaces
'supervisor', # slapgrid uses supervisor to manage processes
......
......@@ -18,4 +18,5 @@ ipv4_local_network = 10.0.0.0/16
[slapproxy]
host = 127.0.0.1
port = 5000
database_uri = :memory: # or /path/to/file
# You could also specify: /path/to/file
database_uri = :memory:
libnetworkcache
===============
The goal of libnetworkcache python library is to abstract the REST calls.
It works as wrapper of python httplib to use the Networkcache HTTP Server.
API
---
So, it must provide 2 methods:
put(file)
''' Upload the file to Networkcache HTTP Server using PUT as HTTP method.'''
get(key)
''' Download the file from Networkcache HTTP Server using GET as HTTP method.'''
......@@ -27,13 +27,13 @@
##############################################################################
from optparse import OptionParser, Option
from xml_marshaller import xml_marshaller
from pwd import getpwnam
import ConfigParser
import grp
import logging
import netaddr
import netifaces
import os
import pwd
import random
import slapos.slap as slap
import socket
......@@ -41,6 +41,34 @@ import subprocess
import sys
import time
class OS(object):
_os = os
def __init__(self, config):
self._dry_run = config.dry_run
self._verbose = config.verbose
self._logger = config.logger
add = self._addWrapper
add('chown')
add('chmod')
add('makedirs')
add('mkdir')
def _addWrapper(self, name):
def wrapper(*args, **kw):
if self._verbose:
arg_list = [repr(x) for x in args] + [
'%s=%r' % (x, y) for x, y in kw.iteritems()]
self._logger.debug('%s(%s)' % (
name,
', '.join(arg_list)
))
if not self._dry_run:
getattr(self._os, name)(*args, **kw)
setattr(self, name, wrapper)
def __getattr__(self, name):
return getattr(self._os, name)
class SlapError(Exception):
"""
......@@ -195,6 +223,8 @@ class Computer:
slap_instance.initializeConnection(config.master_url,
**connection_dict)
slap_computer = slap_instance.registerComputer(self.reference)
if config.dry_run:
return
return slap_computer.updateConfiguration(
xml_marshaller.dumps(_getDict(self)))
......@@ -280,7 +310,7 @@ class Computer:
slapsoft.path = self.software_root
if alter_user:
slapsoft.create()
slapsoft_pw = pwd.getpwnam(slapsoft.name)
slapsoft_pw = getpwnam(slapsoft.name)
os.chown(self.software_root, slapsoft_pw.pw_uid, slapsoft_pw.pw_gid)
os.chmod(self.software_root, 0755)
......@@ -365,7 +395,7 @@ class Partition:
if not os.path.exists(self.path):
os.mkdir(self.path, 0750)
if alter_user:
owner_pw = pwd.getpwnam(owner.name)
owner_pw = getpwnam(owner.name)
os.chown(self.path, owner_pw.pw_uid, owner_pw.pw_gid)
os.chmod(self.path, 0750)
......@@ -402,12 +432,12 @@ class User:
except KeyError:
callAndRead(['groupadd', self.name])
user_parameter_list = ['-d', self.path, '-g', self.name]
user_parameter_list = ['-d', self.path, '-g', self.name, '-s', '/bin/false']
if self.additional_group_list is not None:
user_parameter_list.extend(['-G', ','.join(self.additional_group_list)])
user_parameter_list.append(self.name)
try:
pwd.getpwnam(self.name)
getpwnam(self.name)
except KeyError:
callAndRead(['useradd'] + user_parameter_list)
else:
......@@ -425,7 +455,7 @@ class User:
"""
try:
pwd.getpwnam(self.name)
getpwnam(self.name)
return True
except KeyError:
......@@ -463,7 +493,7 @@ class Tap:
owner_id = int(open(check_file).read().strip())
except Exception:
pass
if (owner_id is None) or (owner_id != pwd.getpwnam(owner.name).pw_uid):
if (owner_id is None) or (owner_id != getpwnam(owner.name).pw_uid):
callAndRead(['tunctl', '-t', self.name, '-u', owner.name])
callAndRead(['ip', 'link', 'set', self.name, 'up'])
......@@ -707,6 +737,10 @@ class Parser(OptionParser):
help="Shall slapformat alter user database [default: True]"),
Option('--alter_network', choices=['True', 'False'],
help="Shall slapformat alter network configuration [default: True]"),
Option("-d", "--dry-run",
default=False,
action="store_true",
help="Don't actually do anything."),
])
def check_args(self):
......@@ -831,7 +865,8 @@ def run(config):
alter_network=config.alter_network)
# Dumping and sending to the erp5 the current configuration
computer.dump(config.computer_xml)
if not config.dry_run:
computer.dump(config.computer_xml)
config.logger.info('Posting information to %r' % config.master_url)
computer.send(config)
except:
......@@ -904,11 +939,17 @@ class Config:
self.logger.error(message)
raise UsageError(message)
if self.alter_user:
self.checkRequiredBinary(['groupadd', 'useradd', 'usermod'])
if not self.dry_run:
if self.alter_user:
self.checkRequiredBinary(['groupadd', 'useradd', 'usermod'])
if self.alter_network:
self.checkRequiredBinary(['ip', 'tunctl'])
# Required, even for dry run
if self.alter_network:
self.checkRequiredBinary(['brctl', 'ip', 'tunctl'])
self.checkRequiredBinary(['brctl'])
if self.dry_run:
root_needed = False
# check root
if root_needed and os.getuid() != 0:
......@@ -937,6 +978,8 @@ class Config:
if self.verbose:
self.logger.setLevel(logging.DEBUG)
self.logger.debug("Verbose mode enabled.")
if self.dry_run:
self.logger.info("Dry-run mode enabled.")
# Calculate path once
self.computer_xml = os.path.abspath(self.computer_xml)
......@@ -944,6 +987,10 @@ class Config:
def main():
"Run default configuration."
global os
global callAndRead
global getpwnam
real_callAndRead = callAndRead
usage = "usage: %s [options] CONFIGURATION_FILE" % sys.argv[0]
try:
......@@ -951,6 +998,33 @@ def main():
options, configuration_file_path = Parser(usage=usage).check_args()
config = Config()
config.setConfig(options, configuration_file_path)
os = OS(config)
if config.dry_run:
def dry_callAndRead(argument_list, raise_on_error=True):
if argument_list == ['brctl', 'show']:
return real_callAndRead(argument_list, raise_on_error)
else:
return 0, ''
callAndRead = dry_callAndRead
real_addSystemAddress = Bridge._addSystemAddress
def fake_addSystemAddress(*args, **kw):
real_addSystemAddress(*args, **kw)
# Fake success
return True
Bridge._addSystemAddress = fake_addSystemAddress
def fake_getpwnam(user):
class result:
pw_uid = 12345
pw_gid = 54321
return result
getpwnam = fake_getpwnam
else:
dry_callAndRead = real_callAndRead
if config.verbose:
def logging_callAndRead(argument_list, raise_on_error=True):
config.logger.debug(' '.join(argument_list))
return dry_callAndRead(argument_list, raise_on_error)
callAndRead = logging_callAndRead
run(config)
except UsageError, err:
print >>sys.stderr, err.msg
......
##############################################################################
#
# Copyright (c) 2010 ViFiB SARL and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import httplib
import os
class NetworkcacheClient(object):
'''
NetworkcacheClient is a wrapper for httplib.
It must implement all the required methods to use the Networkcache HTTP
Server.
- put(file)
- get(key)
'''
def __init__(self, networkcache_url):
# XXX (lucas): Is it required to check if networkcache_url is a valid URL?
self.networkcache_url = networkcache_url
def _start(self):
self.connection = httplib.HTTPConnection(self.networkcache_url)
def _close(self):
self.connection.close()
def put(self, file_content):
'''
Upload the file to the server.
It uses http PUT resquest method.
'''
if file_content is not None:
raise ValueError('File content should not be None.')
self._start()
try:
self.connection.request('PUT', '/', file_content)
result = self.connection.getresponse()
finally:
self._close()
return result
def get(self, key):
'''
Download the file.
It uses http GET request method.
'''
path_info = '/%s' % key
self._start()
try:
self.connection.request('GET', path_info)
result = self.connection.getresponse()
finally:
self._close()
return result
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment