1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 __authors__ = ["Marie-Francoise Incardona", "Olof Svensson", "Jérôme Kieffer"]
29 __contact__ = "svensson@esrf.fr"
30 __license__ = "LGPLv3+"
31 __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
32
33 import os, tempfile, stat, types
34
35 from EDSlot import EDSlot
36 from EDUtilsPath import EDUtilsPath
37 from EDConfiguration import EDConfiguration
38 from EDConfigurationStatic import EDConfigurationStatic
39 from EDUtilsFile import EDUtilsFile
40 from EDStatus import EDStatus
41 from EDAction import EDAction
42 from EDDecorator import deprecated
43 from XSDataCommon import XSDataResult
47 """
48 This is the EDNA plugin main class
49 An EDNA plugin class:
50 - is a configurable entity
51 - has a base name (<date>-<random number>-<base name>)
52 - handles input/output data (setter, getter, checker)
53 - has warning and error messages
54 - has a base and a working directory (both are configurable)
55 The working directory is the folder from which the plugin is launched
56 and should contain all associated files with the plugin execution (edna xml input/output, 3rd party output files)
57 The base directory is the parent directory of the working directory.
58 Example: the working directory of a control plugin is the base directory of the plugins that it invokes,
59 i.e. the plugins working directories has the control plugin working directory as parent.
60 - defines the method that generates an executive summary (user-related output summary) that sub-classes should implement
61 """
62 CONF_BASE_DIR_LABEL = "baseDirectory"
63 CONF_WORKING_DIR_LABEL = "workingDirectory"
64 CONF_TIME_OUT = "timeOut"
65 CONF_WRITE_XML_INPUT_OUTPUT = "writeXMLInputOutput"
66 CONF_WRITE_XML_OUTPUT = "writeXMLOutput"
67 CONF_WRITE_XML_INPUT = "writeXMLInput"
68
70 """
71 Initializes plugin related attributes described above
72 """
73 EDAction.__init__(self)
74 self.__xsPluginItem = None
75 self.__dictXSDataInputClass = {}
76 self.__dictXSDataInput = {}
77 self.__dictXSDataOutput = {}
78 self.__strDefaultInputDataKey = "defaultInputData"
79 self.__strDefaultOutputDataKey = "defaultOutputData"
80 self.__edSlotExportDataOutput = EDSlot()
81 self.__strBaseDirectory = None
82 self.__strWorkingDirectory = None
83 self.__strBaseName = None
84 self.__listExecutiveSummaryLines = []
85 self.__strExecutiveSummarySeparator = "-" * 80
86 self.__listErrorMessages = []
87 self.__listWarningMessages = []
88 self.__isRequiredToHaveConfiguration = False
89 self.__bWriteDataXMLInputOutput = True
90 self.__bWriteDataXMLOutput = True
91 self.__bWriteDataXMLInput = True
92 self.__strPluginId = "%s-%08i" % (self.getClassName(), self.getId())
93 self.strPathDataInput = None
94 self.strPathDataOutput = None
95 self.__bUseWarningInsteadOfError = False
96 self.__edConfiguration = EDConfigurationStatic()
97
98
121
122
131
133 """
134 Checks if output data is available, if not issues a warning and sets an empty XSDataResult as output data
135 Writes xml data output in the working dir (if required)
136 """
137 EDAction.finallyProcess(self, _edObject)
138 if self.__dictXSDataOutput == {}:
139 strWarningMessage = "Output data for plugin %s not set, using XSDataResult as output" % self.getPluginName()
140 self.WARNING(strWarningMessage)
141 self.addWarningMessage(strWarningMessage)
142 self.setDataOutput(XSDataResult())
143 if self.__bWriteDataXMLInputOutput:
144 if self.__bWriteDataXMLOutput:
145 self.writeDataOutput()
146
147
149 """
150 This method calls EDAction.synchronize and if a time-out occurs an error message
151 is added to the list of error messages.
152 """
153 EDAction.synchronize(self)
154 if self.isTimeOut():
155 strErrorMessage = "Timeout when waiting for %s to terminate." % self.getClassName()
156 self.addErrorMessage(strErrorMessage)
157
158 @deprecated
169
170
171 @deprecated
178 configuration = property(getConfiguration, setConfiguration)
179
180
187
188 - def setConfig(self, _dict, _bLocal = False):
199 config = property(getConfig, setConfig)
200
201
202 @deprecated
204 """
205 This method returns a configuration parameter value if a corresponding configuration
206 parameter name can be found in the configuration file.
207
208 If an application wide configuration file is provided via EDApplication it will
209 override the product configuration file.
210
211 The configuration parameter is then searched in the configration file in following order:
212 - If a plugin configuration item exists and the configration parameter name is present it will be used.
213 - Otherwise if a product-wide (e.g. "mxPluginExec") configuration value exists it will be used.
214 """
215 strParameterValue = self.__edConfiguration.getStringValue(self.getPluginName(), _strConfigurationParameterName)
216 self.DEBUG("EDPlugin.getConfigurationParameterValue: %s, %s = %s" % (self.getPluginName(),
217 _strConfigurationParameterName,
218 strParameterValue))
219 return strParameterValue
220
221 @deprecated
223 fParameterValue = None
224 strParameterValue = self.getStringConfigurationParameterValue(_strConfigurationParameterName)
225 try:
226 return float(strParameterValue)
227 except TypeError:
228 return
229 except ValueError:
230 self.ERROR("float() argument must be a string or a number, got %s" % strParameterValue)
231
232
233 @deprecated
235 iParameterValue = None
236 strParameterValue = self.getStringConfigurationParameterValue(_strConfigurationParameterName)
237 try:
238 return int(strParameterValue)
239 except TypeError:
240 return
241 except ValueError:
242 self.ERROR("int() argument must be a string or a number, got %s" % strParameterValue)
243
244
301
302
303 - def execute(self, _edObject=None):
307
308
310 """
311 Should be overridden by the Final Plugin If needed
312 This method should check that the data input are consistent
313 """
314 self.DEBUG("EDPlugin.checkParameters")
315
316
331
332
344
345
397
398
411
412
429
430
445
446
447 dataInput = property(getDataInput, setDataInput, delDataInput, "Property for dataInput")
448
449
451 """
452 Sets the plugin output data for a particular key.
453 If the key is not provided a default key is used.
454
455 If the key is already defined in the dictionary, the corresponding
456 data object is added to a list which contains the already stored object(s).
457 """
458 strDataOutputKey = _strDataOutputKey
459 if (strDataOutputKey is None):
460 strDataOutputKey = self.__strDefaultOutputDataKey
461
462 if (strDataOutputKey == self.__strDefaultOutputDataKey):
463 self.__dictXSDataOutput[ strDataOutputKey ] = _xsDataOutput
464 else:
465
466 if (type(_xsDataOutput) == types.ListType):
467 self.__dictXSDataOutput[ strDataOutputKey ] = _xsDataOutput
468 else:
469
470 if (not strDataOutputKey in self.__dictXSDataOutput.keys()):
471 self.__dictXSDataOutput[ strDataOutputKey ] = []
472 self.__dictXSDataOutput[ strDataOutputKey ].append(_xsDataOutput)
473
474
476 """
477 Returns the Plugin Output Data
478 """
479 oValue = None
480 strDataOutputKey = _strDataOutputKey
481 if (strDataOutputKey is None):
482 strDataOutputKey = self.__strDefaultOutputDataKey
483 if (strDataOutputKey in self.__dictXSDataOutput.keys()):
484 oValue = self.__dictXSDataOutput[ strDataOutputKey ]
485 return oValue
486
487
489 """
490 Returns True if the plugin has the specified Output Data
491 """
492 strDataOutputKey = _strDataOutputKey
493 if (strDataOutputKey is None):
494 strDataOutputKey = self.__strDefaultOutputDataKey
495 if (strDataOutputKey in self.__dictXSDataOutput.keys()):
496 return True
497 else:
498 return False
499
500
502 """
503 Deletes the data output for a particular key.
504 If the key is not provided a default key is used.
505 """
506 strDataOutputKey = _strDataOutputKey
507 if (strDataOutputKey is None):
508 strDataOutputKey = self.__strDefaultOutputDataKey
509 if (strDataOutputKey in self.__dictXSDataOutput.keys()):
510 self.__dictXSDataOutput[ strDataOutputKey ] = None
511 else:
512 strErrorMessage = self.getPluginName() + ".delDataOutput, no output data defined for key: " + _strDataOutputKey
513 self.warning(strErrorMessage)
514 self.addWarningMessage(strErrorMessage)
515
516
517 dataOutput = property(getDataOutput, setDataOutput, delDataOutput, "Property for dataOutput")
518
519
521 """
522 Deprecated
523 Exports the Plugin Output Data to slot
524 """
525 self.DEBUG("EDPlugin.exportDataOutput")
526 self.__edSlotExportDataOutput.call(self.__dictXSDataOutput)
527
528
537
538
540 """
541 This method, which should be implemented by sub-classes, generates an executive summary (user-related output summary).
542 """
543 self.DEBUG("EDPlugin.generateExecutiveSummary")
544
545
547 """
548 Adds an error message to the error messages list
549 """
550 self.__listErrorMessages.append(_strErrorMessage)
551
552
554 """
555 Returns the error messages list
556 OBS! This method is deprecated, please use getListOfErrorMessages instead.
557 """
558 self.warning("Deprecation by Monday 7th June 2010 of EDPlugin, called getErrorMessages")
559 from EDImportLib import EDList
560 return EDList(self.__listErrorMessages)
561
562
564 """
565 Returns the error messages list
566 """
567 return self.__listErrorMessages
568
569
571 """
572 Adds a warning message to the warning messages list
573 """
574 self.DEBUG("EDPlugin.addWarningMessage : " + _strWarningMessage)
575 self.__listWarningMessages.append(_strWarningMessage)
576
577
579 """
580 Returns the warning messages list
581 OBS! This method is deprecated, please use getListOfWarningMessages instead.
582 """
583 self.warning("Deprecation by Monday 7th June 2010 of EDPlugin, called getWarningMessages")
584 from EDImportLib import EDList
585 return EDList(self.__listWarningMessages)
586
587
589 """
590 Returns the warning messages list
591 """
592 return self.__listWarningMessages
593
594
611
612
614 """
615 Writes the output data object(s) into a working dir xml file
616 """
617 self.DEBUG("EDPlugin.writeDataOutput")
618 for strKey in self.__dictXSDataOutput.keys():
619 if (strKey == self.__strDefaultOutputDataKey):
620 xsDataOutput = self.__dictXSDataOutput[ self.__strDefaultOutputDataKey ]
621 if (xsDataOutput is not None):
622 self.strPathDataOutput = os.path.join(self.getWorkingDirectory(), self.compactPluginName(self.getPluginName()) + "_dataOutput.xml")
623 EDUtilsFile.writeFile(self.strPathDataOutput, xsDataOutput.marshal())
624 else:
625 listXSDataOutput = self.__dictXSDataOutput[ strKey ]
626 for iIndex, xsDataOutput in enumerate(listXSDataOutput):
627 if (xsDataOutput is not None):
628 strPathDataOutput = os.path.join(self.getWorkingDirectory(), self.compactPluginName(self.getPluginName()) + "_" + strKey + "_%d_dataOutput.xml" % iIndex)
629 EDUtilsFile.writeFile(strPathDataOutput, xsDataOutput.marshal())
630
631
633 """
634 Returns the plugin base name
635 """
636 if (self.__strBaseName is None):
637 self.__strBaseName = self.createBaseName()
638 return self.__strBaseName
639
640
642 """
643 Sets the plugin base name
644 """
645 self.__strBaseName = self.compactPluginName(_strBaseName)
646 self.setName(self.__strBaseName)
647
648 strWorkingDirPath = os.path.join(self.getBaseDirectory(), self.__strBaseName)
649 if not os.path.isdir(strWorkingDirPath):
650 os.mkdir(strWorkingDirPath)
651 self.setWorkingDirectory(strWorkingDirPath)
652
653
655 """
656 Generates the plugin base name: (<prefix>-<object ID>)
657 """
658
659 strBaseName = "%s-%08d" % (self.compactPluginName(self.getPluginName()), self.getId())
660 strBaseDir = os.path.join(self.getBaseDirectory(), strBaseName)
661
662 try:
663 os.mkdir(strBaseDir)
664 except BaseException, strErrorDetail:
665 self.error("EDPlugin.createBaseName: Could not create base directory %s because of %s" % (strBaseDir, strErrorDetail))
666 self.warning("EDPlugin.createBaseName: Trying to create alternative base directory...")
667 self.writeErrorTrace()
668 strTempDir = tempfile.mkdtemp(prefix=strBaseName, dir=self.getBaseDirectory())
669 os.chmod(strTempDir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
670 strBaseName = os.path.split(strTempDir)[1]
671 strBaseDir = os.path.join(self.getBaseDirectory(), strBaseName)
672 self.warning("EDPlugin.createBaseName: Alternative base directory created: %s" % strBaseDir)
673 self.DEBUG("EDPlugin.createBaseName : Directory created = " + strBaseDir)
674 return strBaseName
675
676
678 """
679 The prefix is constructed from the plugin name with the following renaming:
680 EDPlugin -> ""
681 EDPluginExec -> ""
682 EDPluginControl -> "Control"
683 """
684 strCompactName = _pluginName
685 if (strCompactName.startswith("EDPluginExec")):
686 strCompactName = strCompactName[12:]
687 elif (strCompactName.startswith("EDPluginControl")):
688 strCompactName = strCompactName[8:]
689 elif (strCompactName.startswith("EDPlugin")):
690 strCompactName = strCompactName[8:]
691 return strCompactName
692
693
695 """
696 Sets the plugin base directory
697 """
698 self.DEBUG("EDPlugin.setBaseDirectory : " + _strBaseDirectory)
699 if (not os.path.isdir(_strBaseDirectory)):
700 self.DEBUG("EDPlugin.setBaseDirectory: base directory %s does dot yet exist! Creating it." % _strBaseDirectory)
701 os.mkdir(_strBaseDirectory)
702 self.__strBaseDirectory = _strBaseDirectory
703
704
706 """
707 Returns the plugin base directory
708 """
709 self.DEBUG("EDPlugin.getBaseDirectory : %s" % self.__strBaseDirectory)
710 if (self.__strBaseDirectory is None):
711 self.__strBaseDirectory = os.getcwd()
712 return self.__strBaseDirectory
713
714
716 """
717 Sets the plugin working directory
718 """
719 self.DEBUG("EDPlugin.setWorkingDirectory : " + _strWorkingDirectory)
720 self.__strWorkingDirectory = _strWorkingDirectory
721 if not os.path.isdir(_strWorkingDirectory):
722 self.DEBUG("EDPlugin.setWorkingDirectory, creating working directory %s." % _strWorkingDirectory)
723 os.mkdir(self.__strWorkingDirectory)
724
725
727 """
728 Returns the plugin base directory
729 """
730 self.DEBUG("EDPlugin.getWorkingDirectory : %s" % self.__strWorkingDirectory)
731 returnValue = None
732 if (self.__strWorkingDirectory is not None):
733 returnValue = self.__strWorkingDirectory
734 return returnValue
735
736
738 """
739 Checks that a mandatory parameter exists in the data
740 If not, an error message is added in the list and the plugin fails
741 """
742 if _xsData is None or (hasattr(_xsData, '__len__') and len(_xsData) == 0):
743 strErrorMessage = "%s: input parameter is missing: %s" % (self.getPluginName(), _strParamName)
744 self.error(strErrorMessage)
745 self.addErrorMessage(strErrorMessage)
746 raise RuntimeError, strErrorMessage
747
748
750 """
751 Checks that a specific parameter exists in the data
752 If not, a warning message is added in the list
753 """
754 if(_xsData == None):
755 strWarningMessage = "%s: input parameter is missing: %s" % (self.getPluginName(), _strParamName)
756 self.warning(strWarningMessage)
757 self.addWarningMessage(strWarningMessage)
758
759
761 """
762 Add a line to the executive summary string.
763 """
764 self.DEBUG("EDPlugin.addExecutiveSummaryLine : %r" % _strExecutiveSummaryLine)
765 strExecutiveSummaryLine = _strExecutiveSummaryLine
766 if (not strExecutiveSummaryLine == ""):
767 if (strExecutiveSummaryLine[-1] == "\n"):
768 strExecutiveSummaryLine = strExecutiveSummaryLine[:-1]
769 self.__listExecutiveSummaryLines.append(strExecutiveSummaryLine)
770
771
773 """
774 Adds a separator to split the executive summary into different parts
775 Default is a dotted line
776 """
777 strSeparator = _strSeparator
778 if (strSeparator is None):
779 strSeparator = self.__strExecutiveSummarySeparator
780
781 if self.__listExecutiveSummaryLines != []:
782 if self.__listExecutiveSummaryLines[-1] != strSeparator:
783 self.addExecutiveSummaryLine(strSeparator)
784 else:
785 self.addExecutiveSummaryLine(strSeparator)
786
787
789 """
790 Returns the executive summary (list of text lines)
791 """
792 return self.__listExecutiveSummaryLines
793
794
801
804
805
808
809
812
813
815 return self.__dictXSDataOutput.keys()
816
817
820
821
823 return self.__strDefaultOutputDataKey
824
825
827 return self.__strExecutiveSummarySeparator
828
829
831 """
832 If the return value from this method is true, the plugin
833 is required to have a configuration in order to be executed in a
834 plugin execution test case.
835 @return: RequiredToHaveConfiguration
836 @rtype: boolean
837 """
838 return self.__isRequiredToHaveConfiguration
839
840
842 """
843 Sets or unsets the plugin to be required to have a configuration
844 for execution in a plugin execution test case.
845 plugin execution test case.
846 @param _bValue: RequiredToHaveConfiguration
847 @type: boolean
848 """
849 self.__isRequiredToHaveConfiguration = _bValue
850
851
859
867
869 """
870 Sets or unsets the plugin to write XML output files.
871 @param _bValue: WriteDataXMLOutput
872 @type: boolean
873 """
874 self.__bWriteDataXMLOutput = _bValue
875
876
878 """
879 Sets or unsets the plugin to use warning messages also for error messages.
880 @param _bValue: UseWarningInsteadOfError
881 @type: boolean
882 """
883 self.__bUseWarningInsteadOfError = _bValue
884
885 - def error(self, _strErrorMessage):
886 """
887 Overloaded from EDLogging. If self.__bUseWarningInsteadOfError is True
888 a warning message is issued instead of an error message.
889 """
890 if self.__bUseWarningInsteadOfError:
891 self.warning(_strErrorMessage)
892 else:
893 EDAction.error(self, _strErrorMessage)
894
895 - def ERROR(self, _strErrorMessage):
896 """
897 Uses the overloaded self.error method above.
898 """
899 self.error(_strErrorMessage)
900