summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Safranek <jsafrane@redhat.com>2013-03-25 15:44:49 +0100
committerJan Safranek <jsafrane@redhat.com>2013-03-25 15:44:49 +0100
commit178d09b21fff46f96db444608e2005182a232299 (patch)
tree37717f488b0067912b3c261dc5e5f05da9b5227b
parent81ffc78618c0518f2e0829a6f9c1a8f2acff6ebd (diff)
parent6565f1261bf5ec326e780a2b2becbb8200c63b7c (diff)
downloadopenlmi-providers-178d09b21fff46f96db444608e2005182a232299.tar.gz
openlmi-providers-178d09b21fff46f96db444608e2005182a232299.tar.xz
openlmi-providers-178d09b21fff46f96db444608e2005182a232299.zip
Merge branch 'master' of ssh://git.fedorahosted.org/git/openlmi-providers
-rw-r--r--CMakeLists.txt1
-rw-r--r--README3
-rw-r--r--mof/LMI_Jobs.mof125
-rw-r--r--mof/LMI_Qualifiers.mof21
-rw-r--r--mof/LMI_Software.mof846
-rw-r--r--mof/LMI_Software.reg73
-rwxr-xr-xopenlmi-mof-register48
-rwxr-xr-xsrc/software/cli/software.py284
-rw-r--r--src/software/openlmi/software/LMI_AffectedSoftwareJobElement.py235
-rw-r--r--src/software/openlmi/software/LMI_AssociatedSoftwareJobMethodResult.py248
-rw-r--r--src/software/openlmi/software/LMI_HostedSoftwareCollection.py238
-rw-r--r--src/software/openlmi/software/LMI_HostedSoftwareIdentityResource.py (renamed from src/software/openlmi/software/LMI_SoftwarePackageChecks.py)141
-rw-r--r--src/software/openlmi/software/LMI_InstalledSoftwareIdentity.py309
-rw-r--r--src/software/openlmi/software/LMI_MemberOfSoftwareCollection.py280
-rw-r--r--src/software/openlmi/software/LMI_OwningSoftwareJobElement.py223
-rw-r--r--src/software/openlmi/software/LMI_ResourceForSoftwareIdentity.py317
-rw-r--r--src/software/openlmi/software/LMI_SoftwareFileCheck.py247
-rw-r--r--src/software/openlmi/software/LMI_SoftwareIdentity.py188
-rw-r--r--src/software/openlmi/software/LMI_SoftwareIdentityResource.py (renamed from src/software/openlmi/software/LMI_SoftwarePackage.py)202
-rw-r--r--src/software/openlmi/software/LMI_SoftwareInstallationJob.py380
-rw-r--r--src/software/openlmi/software/LMI_SoftwareInstallationService.py783
-rw-r--r--src/software/openlmi/software/LMI_SoftwareInstalledPackage.py451
-rw-r--r--src/software/openlmi/software/LMI_SoftwareMethodResult.py162
-rw-r--r--src/software/openlmi/software/LMI_SystemSoftwareCollection.py173
-rw-r--r--src/software/openlmi/software/__init__.py5
-rw-r--r--src/software/openlmi/software/cimom_entry.py62
-rw-r--r--src/software/openlmi/software/core/AffectedSoftwareJobElement.py205
-rw-r--r--src/software/openlmi/software/core/ComputerSystem.py26
-rw-r--r--src/software/openlmi/software/core/Error.py457
-rw-r--r--src/software/openlmi/software/core/Identity.py270
-rw-r--r--src/software/openlmi/software/core/IdentityResource.py650
-rw-r--r--src/software/openlmi/software/core/InstallationJob.py610
-rw-r--r--src/software/openlmi/software/core/InstallationService.py729
-rw-r--r--src/software/openlmi/software/core/MethodResult.py84
-rw-r--r--src/software/openlmi/software/core/SoftwareFileCheck.py515
-rw-r--r--src/software/openlmi/software/core/SoftwareInstalledPackage.py39
-rw-r--r--src/software/openlmi/software/core/SoftwarePackage.py412
-rw-r--r--src/software/openlmi/software/core/SoftwarePackageChecks.py32
-rw-r--r--src/software/openlmi/software/core/SystemCollection.py73
-rw-r--r--src/software/openlmi/software/core/__init__.py64
-rw-r--r--src/software/openlmi/software/util/__init__.py46
-rw-r--r--src/software/openlmi/software/util/singletonmixin.py562
-rw-r--r--src/software/openlmi/software/yumdb/__init__.py488
-rw-r--r--src/software/openlmi/software/yumdb/errors.py58
-rw-r--r--src/software/openlmi/software/yumdb/jobmanager.py413
-rw-r--r--src/software/openlmi/software/yumdb/jobs.py463
-rw-r--r--src/software/openlmi/software/yumdb/packagecheck.py8
-rw-r--r--src/software/openlmi/software/yumdb/packageinfo.py30
-rw-r--r--src/software/openlmi/software/yumdb/process.py690
-rw-r--r--src/software/openlmi/software/yumdb/repository.py190
-rw-r--r--src/software/openlmi/software/yumdb/util.py97
-rw-r--r--src/software/setup.py2
-rw-r--r--src/software/test/base.py (renamed from src/software/test/common.py)199
-rw-r--r--src/software/test/package.py130
-rw-r--r--src/software/test/repository.py175
-rw-r--r--src/software/test/rpmcache.py426
-rwxr-xr-xsrc/software/test/run.py23
-rwxr-xr-xsrc/software/test/test_hosted_software_collection.py157
-rwxr-xr-xsrc/software/test/test_hosted_software_identity_resource.py165
-rwxr-xr-xsrc/software/test/test_installed_software_identity.py253
-rwxr-xr-xsrc/software/test/test_member_of_software_collection.py155
-rwxr-xr-xsrc/software/test/test_resource_for_software_identity.py176
-rwxr-xr-xsrc/software/test/test_software_file_check.py527
-rwxr-xr-xsrc/software/test/test_software_identity.py154
-rwxr-xr-xsrc/software/test/test_software_identity_resource.py207
-rwxr-xr-xsrc/software/test/test_software_installed_package.py351
-rwxr-xr-xsrc/software/test/test_software_package.py153
-rwxr-xr-xsrc/software/test/test_system_software_collection.py86
-rw-r--r--src/software/test/util.py82
-rw-r--r--tools/CMakeLists.txt3
-rwxr-xr-xtools/class2uml.py219
-rw-r--r--[-rwxr-xr-x]tools/openlmi-doc-class2rst (renamed from tools/class2rst.py)371
-rw-r--r--tools/openlmi-doc-class2uml143
73 files changed, 12747 insertions, 4666 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d937cb0..230ed26 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ find_package(KonkretCMPI REQUIRED)
add_subdirectory(src)
add_subdirectory(mof)
+add_subdirectory(tools)
install(PROGRAMS openlmi-mof-register DESTINATION bin)
install(PROGRAMS openlmi-register-pegasus DESTINATION libexec)
diff --git a/README b/README
index 888201b..7e17e45 100644
--- a/README
+++ b/README
@@ -47,6 +47,9 @@ Following providers are part of this sub-project:
- start, stop, restart
- enable, disable
+* Software
+ Allows to install, remove, list and update packages.
+ Requires python 2.6+ and yum.
*******************************************************************************
* Build Dependencies *
diff --git a/mof/LMI_Jobs.mof b/mof/LMI_Jobs.mof
new file mode 100644
index 0000000..b17e833
--- /dev/null
+++ b/mof/LMI_Jobs.mof
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Jan Safranek <jsafrane@redhat.com>
+ * Michal Minar <miminar@redhat.com
+ */
+
+#pragma include ("LMI_Qualifiers.mof")
+
+class LMI_AffectedJobElement : CIM_AffectedJobElement
+{
+};
+
+class LMI_AssociatedJobMethodResult : CIM_AssociatedJobMethodResult
+{
+};
+
+class LMI_OwningJobElement : CIM_OwningJobElement
+{
+};
+
+class LMI_MethodResult : CIM_MethodResult
+{
+};
+
+
+class LMI_ConcreteJob : CIM_ConcreteJob
+{
+ [ Implemented(true), Override("DeleteOnCompletion") ]
+ boolean DeleteOnCompletion;
+ [ Implemented(true), Override("ElapsedTime") ]
+ datetime ElapsedTime;
+ [ Implemented(true), Override("JobState") ]
+ uint16 JobState;
+ [ Implemented(true), Override("LocalOrUtcTime") ]
+ uint16 LocalOrUtcTime;
+ [ Implemented(true), Override("Name") ]
+ string Name;
+ [ Implemented(true), Override("OperationalStatus")]
+ uint16 OperationalStatus[];
+ [ Implemented(true), Override("PercentComplete") ]
+ uint16 PercentComplete;
+ [ Implemented(true), Override("StartTime") ]
+ datetime StartTime;
+ [ Implemented(true), Override("TimeBeforeRemoval") ]
+ datetime TimeBeforeRemoval;
+ [ Implemented(true), Override("TimeOfLastStateChange") ]
+ datetime TimeOfLastStateChange;
+ [ Implemented(true), Override("TimeSubmitted")]
+ datetime TimeSubmitted;
+
+ [ Implemented(true), Override("GetError"),
+ Deprecated{"CIM_ConcreteJob.GetErrors"} ]
+ uint32 GetError(
+ [OUT, Description (
+ "If the OperationalStatus on the Job is not \"OK\", "
+ "then this method will return a CIM Error instance. "
+ "Otherwise, when the Job is \"OK\", null is "
+ "returned." ),
+ EmbeddedInstance ( "CIM_Error" )]
+ string Error);
+
+ [Implemented(true), Override("GetErrors")]
+ uint32 GetErrors(
+ [OUT, Description (
+ "If the OperationalStatus on the Job is not \"OK\", "
+ "then this method will return one or more CIM Error "
+ "instance(s). Otherwise, when the Job is \"OK\", "
+ "null is returned." ),
+ EmbeddedInstance ( "CIM_Error" )]
+ string Errors[]);
+
+ [Implemented(true), Override("RequestStateChange")]
+ uint32 RequestStateChange(
+ [IN, Description (
+ "RequestStateChange changes the state of a job. The "
+ "possible values are as follows: \n"
+ "Start (2) changes the state to \'Running\'. \n"
+ "Suspend (3) stops the job temporarily. The "
+ "intention is to subsequently restart the job with "
+ "\'Start\'. It might be possible to enter the "
+ "\'Service\' state while suspended. (This is "
+ "job-specific.) \n"
+ "Terminate (4) stops the job cleanly, saving data, "
+ "preserving the state, and shutting down all "
+ "underlying processes in an orderly manner. \n"
+ "Kill (5) terminates the job immediately with no "
+ "requirement to save data or preserve the state. \n"
+ "Service (6) puts the job into a vendor-specific "
+ "service state. It might be possible to restart the "
+ "job." ),
+ ValueMap { "2", "3", "4", "5", "6", "7..32767",
+ "32768..65535" },
+ Values { "Start", "Suspend", "Terminate", "Kill",
+ "Service", "DMTF Reserved", "Vendor Reserved" }]
+ uint16 RequestedState,
+ [IN, Description (
+ "A timeout period that specifies the maximum amount "
+ "of time that the client expects the transition to "
+ "the new state to take. The interval format must be "
+ "used to specify the TimeoutPeriod. A value of 0 or "
+ "a null parameter indicates that the client has no "
+ "time requirements for the transition. \n"
+ "If this property does not contain 0 or null and "
+ "the implementation does not support this "
+ "parameter, a return code of \'Use Of Timeout "
+ "Parameter Not Supported\' must be returned." )]
+ datetime TimeoutPeriod);
+
+};
diff --git a/mof/LMI_Qualifiers.mof b/mof/LMI_Qualifiers.mof
index 5a53ff2..d6b11ec 100644
--- a/mof/LMI_Qualifiers.mof
+++ b/mof/LMI_Qualifiers.mof
@@ -1,3 +1,24 @@
+/*
+ * Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Jan Safranek <jsafrane@redhat.com>
+ */
+
/* 'Implemented' qualifier denotes the properties and method that are implemented
* by appropriate OpenLMI provider.
*/
diff --git a/mof/LMI_Software.mof b/mof/LMI_Software.mof
index ad99250..6538f18 100644
--- a/mof/LMI_Software.mof
+++ b/mof/LMI_Software.mof
@@ -21,295 +21,593 @@
#pragma locale ("en_US")
//#pragma namespace ("root/cimv2")
-[ Description (
- "RPM package installed on particular computer system with"
- " YUM (The Yellowdog Updated, Modified) package manager.")
-]
-class LMI_SoftwarePackage : CIM_SoftwareElement {
-
- [ Description (
- "The release number (referred to in some older documentation as a"
- " \"vepoch\") is how the maintainer marks build revisions, starting"
- " from 1. When a minor change (spec file changed, patch added/removed)"
- " occurs, or a package is rebuilt to use newer headers or libraries,"
- " the release number should be incremented. If a major change"
- " (new version of the software being packaged) occurs, the version"
- " number should be changed to reflect the new software version,"
- " and the release number should be reset to 1.")
- ]
- string Release;
-
- [ Description (
- "It is important to be careful with the version of post-release"
- " scheme, to ensure that package ordering is correct. It may be"
- " necessary to use Epoch to ensure that the current package is"
- " considered newer than the previous package.")
- ]
- uint16 Epoch;
-
- [ Description (
- "Architecture name, that package is compiled for. In case of no"
- "architecture dependency, this will contain \"noarch\".")
- ]
- string Architecture;
-
- [ Description (
- "A software license is a legal instrument (usually by way of contract"
- " law) governing the usage or redistribution of software."
- " All software is copyright protected, except material in the public"
- " domain. Contractual confidentiality is another way of protecting"
- " software. A typical software license grants an end-user permission"
- " to use one or more copies of software in ways where such a use would"
- " otherwise potentially constitute copyright infringement of the"
- " software owner's exclusive rights under copyright law. List of"
- " possible values is based on list of licences approved by the"
- " Free Software Foundation and OSI. The string is obtained from RPM"
- " package." )
- ]
- string License;
-
- [ Description (
- "Denotes the package group, in which the package belongs.")
- ]
- string Group;
-
- [ Description (
- "Size of RPM package in Bytes" ) ]
- uint64 Size;
-
- [ Description (
- "Will install available package."),
- ValueMap { "0", "1", "2" },
- Values { "Already installed", "Successful installation", "Failed" } ]
- uint32 Install(
- [ IN(false), OUT, Description (
- "The reference to newly installed package, if installation was"
- " successful. Reference to current package if it is already"
- " installed and Null otherwise.") ]
- LMI_SoftwareInstalledPackage REF Installed);
-
- [ Description (
- "Will uninstall installed package."),
- ValueMap { "0", "1", "2" },
- Values { "Not installed", "Successful removal", "Failed" } ]
- uint32 Remove();
+class LMI_SoftwareIdentity : CIM_SoftwareIdentity {
+
+ [Implemented(true), Override("InstanceID"), Description(
+ "Unique identifier for installed or available package."
+ " It's composed of OrgID and LocalID separated by ':', where"
+ " <OrgID> is LMI and LocalID is PKG:<PKG_NEVRA>. <PKG_NEVRA>"
+ " is a string representing rpm package. Letters in NEVRA stand"
+ " for name, epoch, version, release and architecture.")]
+ string InstanceID;
+
+ [Implemented(true), Override("Caption"), Description("Package's summary.")]
+ string Caption;
+
+ [Implemented(true), Override("Classifications")]
+ uint16 Classifications[];
+
+ [Implemented(true), Override("Description"), Description(
+ "Package's description.")]
+ string Description;
+
+ [Implemented(true), Override("ElementName"), Description(
+ "Package's NEVRA string. That is also part of InstanceID.")]
+ string ElementName;
+
+ [Implemented(true), Override("InstallDate")]
+ datetime InstallDate;
+
+ [Implemented(true), Override("IsEntity")]
+ boolean IsEntity;
+
+ [Implemented(true), Override("Name"), Description(
+ "Name of package. This does not uniquely identify package"
+ " installed on computer system.")]
+ string Name;
+
+ [Implemented(true), Override("TargetTypes")]
+ string TargetTypes[];
+
+ [Implemented(true), Override("VersionString"), Description(
+ "Package's EVRA, in format: "
+ "<epoch>:<version>-<release>.<architecture>")]
+ string VersionString;
+
+ [Implemented(true), Description("Package's epoch.")]
+ uint32 Epoch;
+
+ [Implemented(true), Description("Package's version.")]
+ string Version;
+
+ [Implemented(true), Description("Package's release.")]
+ string Release;
+
+ [Implemented(true), Description("Package's architecture.")]
+ string Architecture;
+
+};
+
+class LMI_SystemSoftwareCollection : CIM_SystemSpecificCollection {
+
+ [Implemented(true), Override("InstanceID")]
+ string InstanceID;
+
+ [Implemented(true), Override("Caption")]
+ string Caption;
};
-[ Description (
- "Identifies a file contained by RPM package. It's located"
- " in directory identified in FileName. The Invoke methods"
- " check for file existence and whether its attributes match"
- " those given by RPM package.")
-]
-class LMI_SoftwareFileCheck : CIM_FileSpecification {
+[Association]
+class LMI_HostedSoftwareCollection : CIM_HostedCollection {
- [ Key, Description (
- "Absolute path of file being checked.") ]
+ [Override("Antecedent")]
+ Linux_ComputerSystem REF Antecedent;
+
+ [Override("Dependent")]
+ LMI_SystemSoftwareCollection REF Dependent;
+
+};
+
+[Association]
+class LMI_MemberOfSoftwareCollection : CIM_MemberOfCollection {
+
+ [Override("Collection")]
+ LMI_SystemSoftwareCollection REF Collection;
+
+ [Override("Member")]
+ LMI_SoftwareIdentity REF Member;
+
+};
+
+[Association]
+class LMI_InstalledSoftwareIdentity : CIM_InstalledSoftwareIdentity {
+
+ [Override("InstalledSoftware")]
+ LMI_SoftwareIdentity REF InstalledSoftware;
+
+ [Override("System")]
+ Linux_ComputerSystem REF System;
+
+};
+
+class LMI_SoftwareIdentityResource : CIM_SoftwareIdentityResource {
+
+ [Implemented(true), Override("CreationClassName")]
+ string CreationClassName;
+
+ [Implemented(true), Override("Name"), Description(
+ "Repository id. A unique name representing repository of"
+ " system.")]
string Name;
- [ Description (
- "Composition of RPM's name and absolute file path."
- " It's format is \"rpm_name#path\"") ]
- string CheckID;
-
- [ Override("SoftwareElementID"), Description (
- "RPM's nevra. This means: "
- "\"name-[epoch:]version-release.architecture\"."
- " If epoch part is missing, a \"0:\" is assumed." ) ]
- string SoftwareElementID;
-
- [ Override("Version"), Description (
- "Version of packaged software as listed by RPM package.") ]
- string Version;
-
- [ Description (
- "True, if file is present in file system.")]
- boolean FileExists;
-
- [ Override("FileSize"),
- Description("Size of installed file in Bytes.") ]
- uint64 FileSize;
- [ Units("KiloBytes"),
- Description("File size in Bytes from RPM database.") ]
- uint64 ExpectedFileSize;
-
- [ Description("File mode of installed file as a number.") ]
- uint32 FileMode;
- [ Description("File mode as a number given by RPM database.") ]
- uint32 ExpectedFileMode;
-
- [ Description (
- "File mode of installed file as an array of permissions."
- " Each value represents a bit position in file mode."),
- ValueMap { "0", "1", "2" //XWR Other
- , "3", "4", "5" //XWR Group
- , "6", "7", "8" //XWR User
- , "9","10","11" //Sticky, SGID, SUID
- },
- Values { "Execute Other", "Write Other", "Read Other"
- , "Execute Group", "Write Group", "Read Group"
- , "Execute User" , "Write User" , "Read User"
- , "Sticky", "SGID", "SUID"
- } ]
- uint8 FileModeFlags[];
- [ Description (
- "File mode as an array of permissions of file from RPM database."
- " Each value represents a bit position in file mode."),
- ValueMap { "0", "1", "2" //XWR Other
- , "3", "4", "5" //XWR Group
- , "6", "7", "8" //XWR User
- , "9","10","11" //Sticky, SGID, SUID
- },
- Values { "Execute Other", "Write Other", "Read Other"
- , "Execute Group", "Write Group", "Read Group"
- , "Execute User" , "Write User" , "Read User"
- , "Sticky", "SGID", "SUID"
- } ]
- uint8 ExpectedFileModeFlags[];
-
- [ Override("MD5Checksum"),
- Description("MD5 checksum of installed file.") ]
- string MD5Checksum;
- [ Description("Checksum of installed file."
- " Hash algorithm used can be obtained with ChecksumType property.") ]
- string FileChecksum;
- [ Description("Checksum of file from RPM database."
- " Hash algorithm used can be obtained with ChecksumType property.") ]
- string ExpectedFileChecksum;
-
- [ Description("Number of hash algorithm according to RFC4880."
- " This algorithm is used for making checksums of RPM files and"
- " packages. This algorithm is same for the whole RPM database."),
- ValueMap { "0", "1", "2", "3"
- , "8", "9","10","11" },
- Values { "UNKNOWN", "MD5", "SHA-1", "RIPE-MD/160"
- , "SHA256", "SHA384", "SHA512", "SHA224" } ]
- uint16 FileChecksumType;
-
- [ Description("User ID of installed file.") ]
- uint32 FileUserID;
- [ Description("User ID of file from RPM database.") ]
- uint32 ExpectedFileUserID;
-
- [ Description("Group ID of installed file.") ]
- uint32 FileGroupID;
- [ Description("Group ID of file from RPM database.") ]
- uint32 ExpectedFileGroupID;
-
- [ Description("Time of last modification of installed file as number a"
- " of secodns since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).") ]
- uint64 LastModificationTime;
- [ Description("Time of last modification of file from RPM database as"
- " a number of secodns since the Epoch,"
- " 1970-01-01 00:00:00 +0000 (UTC).") ]
- uint64 ExpectedLastModificationTime;
-
- [ Description("Target destination of symbolic link as returned by"
- " readline. If file is not a symbolic link, NULL is returned.") ]
- string LinkTarget;
- [ Description("Target destination of symbolic link from RPM database"
- " as returned by readline. If file is not a symbolic link,"
- " NULL is returned.") ]
- string ExpectedLinkTarget;
-
- [ Description("File type."),
- ValueMap { "0", "1", "2", "3"
- , "4", "5", "6" },
- Values { "Unknown", "File", "Directory", "Symlink"
- , "FIFO", "Character Device", "Block Device" } ]
- uint16 FileType;
- [ Description("File type of file in RPM database."),
- ValueMap { "0", "1", "2", "3"
- , "4", "5", "6" },
- Values { "Unknown", "File", "Directory", "Symlink"
- , "FIFO", "Character Device", "Block Device" } ]
- uint16 ExpectedFileType;
+ [Implemented(true), Override("SystemCreationClassName")]
+ string SystemCreationClassName;
+
+ [Implemented(true), Override("SystemName")]
+ string SystemName;
+
+ [Implemented(true), Override("AccessContext")]
+ uint16 AccessContext;
+
+ [Implemented(true), Override("AccessInfo")]
+ string AccessInfo;
+
+ [Implemented(true), Override("AvailableRequestedStates")]
+ uint16 AvailableRequestedStates[];
+
+ [Implemented(true), Override("Caption"), Description(
+ "A human readable string describing the repository.")]
+ string Caption;
+
+ [Implemented(true), Description(
+ "Relative cost of accessing this repository."
+ " Useful for weighing one repo's packages as greater/less"
+ " than any other.")]
+ sint32 Cost;
+
+ [Implemented(true), Override("Description")]
+ string Description;
+
+ [Implemented(true), Override("ElementName")]
+ string ElementName;
+
+ [Implemented(true), Override("EnabledDefault")]
+ uint16 EnabledDefault;
+
+ [Implemented(true), Override("EnabledState")]
+ uint16 EnabledState;
+
+ [Implemented(true), Override("ExtendedResourceType")]
+ uint16 ExtendedResourceType;
+
+ [Implemented(true), Override("Generation")]
+ uint64 Generation;
+
+ [Implemented(true), Override("HealthState")]
+ uint16 HealthState;
+
+ [Implemented(true), Description(
+ "Whether or not a GPG signature check should be performed"
+ " on the packages gotten from this repository.")]
+ boolean GPGCheck;
+
+ [Implemented(true), Override("InfoFormat")]
+ uint16 InfoFormat;
+
+ [Implemented(true), Override("InstanceID")]
+ string InstanceID;
+
+ [Implemented(true), Description(
+ "URL to a file containing list of base URLS to mirrors"
+ " of this repository. http://, ftp:// and file:// schemas"
+ " are supported. This can contain special variables"
+ " prefixed with $, which are substituted for system values."
+ " These include $releasever - defaults to the version of"
+ " \"redhat-release\" package, $arch - architecture of system,"
+ " $basearch - base architecture of system ($arch == \"i686\", then"
+ " $basearch == \"i386\", $uuid - unique but persisent uuid for this"
+ " machine.")]
+ string MirrorList;
+
+ [Implemented(true), Override("OperationalStatus")]
+ uint16 OperationalStatus[];
+
+ [Implemented(true), Override("OtherAccessContext")]
+ string OtherAccessContext;
+
+ [Implemented(true), Override("PrimaryStatus")]
+ uint16 PrimaryStatus;
+
+ [Implemented(true), Description(
+ "Whether or not a GPG signature check should be performed"
+ " on the repodata from this repository.")]
+ boolean RepoGPGCheck;
+
+ [Implemented(true), Override("RequestedState")]
+ uint16 RequestedState;
+
+ [Implemented(true), Override("ResourceType")]
+ uint16 ResourceType;
+
+ [Implemented(true), Override("StatusDescriptions")]
+ string StatusDescriptions[];
+
+ [Implemented(true), Override("TimeOfLastStateChange")]
+ datetime TimeOfLastStateChange;
+
+ [Implemented(true), Description(
+ "Time of the repository's last update on server.")]
+ datetime TimeOfLastUpdate;
+
+ [Implemented(true), Override("TransitioningToState")]
+ uint16 TransitioningToState;
+
+ [Implemented(true), Override("RequestStateChange")]
+ uint32 RequestStateChange(
+ [IN, Description (
+ "The state requested for the element. This "
+ "information will be placed into the RequestedState "
+ "property of the instance if the return code of the "
+ "RequestStateChange method is 0 (\'Completed with "
+ "No Error\'), or 4096 (0x1000) (\'Job Started\'). "
+ "Refer to the description of the EnabledState and "
+ "RequestedState properties for the detailed "
+ "explanations of the RequestedState values." ),
+ ValueMap { "2", "3", "4", "6", "7", "8", "9", "10",
+ "11", "..", "32768..65535" },
+ Values { "Enabled", "Disabled", "Shut Down", "Offline",
+ "Test", "Defer", "Quiesce", "Reboot", "Reset",
+ "DMTF Reserved", "Vendor Reserved" },
+ ModelCorrespondence {
+ "CIM_EnabledLogicalElement.RequestedState" }]
+ uint16 RequestedState,
+ [IN ( false ), OUT, Description (
+ "May contain a reference to the ConcreteJob created "
+ "to track the state transition initiated by the "
+ "method invocation." )]
+ CIM_ConcreteJob REF Job,
+ [IN, Description (
+ "A timeout period that specifies the maximum amount "
+ "of time that the client expects the transition to "
+ "the new state to take. The interval format must be "
+ "used to specify the TimeoutPeriod. A value of 0 or "
+ "a null parameter indicates that the client has no "
+ "time requirements for the transition. \n"
+ "If this property does not contain 0 or null and "
+ "the implementation does not support this "
+ "parameter, a return code of \'Use Of Timeout "
+ "Parameter Not Supported\' shall be returned." )]
+ datetime TimeoutPeriod);
+
+};
+
+[Association]
+class LMI_ResourceForSoftwareIdentity : CIM_SAPAvailableForElement {
+
+ [Override("AvailableSAP")]
+ LMI_SoftwareIdentityResource REF AvailableSAP;
+
+ [Override("ManagedElement")]
+ LMI_SoftwareIdentity REF ManagedElement;
+
+};
+
+[Association]
+class LMI_HostedSoftwareIdentityResource : CIM_HostedAccessPoint {
+
+ [Override("Antecedent")]
+ Linux_ComputerSystem REF Antecedent;
+
+ [Override("Dependent")]
+ LMI_SoftwareIdentityResource REF Dependent;
+
+};
+
+class LMI_SoftwareInstallationService : CIM_SoftwareInstallationService {
+
+ [Implemented(true), Override("CreationClassName")]
+ string CreationClassName;
+
+ [Implemented(true), Override("Name")]
+ string Name;
+
+ [Implemented(true), Override("SystemCreationClassName")]
+ string SystemCreationClassName;
+
+ [Implemented(true), Override("SystemName")]
+ string SystemName;
+
+ [Implemented(true), Override("Caption")]
+ string Caption;
+
+ [Implemented(true), Override("CommunicationStatus")]
+ uint16 CommunicationStatus;
+
+ [Implemented(true), Override("Description")]
+ string Description;
+
+ [Implemented(true), Override("DetailedStatus")]
+ uint16 DetailedStatus;
+
+ [Implemented(true), Override("EnabledDefault")]
+ uint16 EnabledDefault;
+
+ [Implemented(true), Override("EnabledState")]
+ uint16 EnabledState;
+
+ [Implemented(true), Override("HealthState")]
+ uint16 HealthState;
+
+ [Implemented(true), Override("InstanceID")]
+ string InstanceID;
+
+ [Implemented(true), Override("OperatingStatus")]
+ uint16 OperatingStatus;
+
+ [Implemented(true), Override("OperationalStatus")]
+ uint16 OperationalStatus[];
+
+ [Implemented(true), Override("PrimaryStatus")]
+ uint16 PrimaryStatus;
+
+ [Implemented(true), Override("PrimaryStatus")]
+ uint16 RequestedState;
+
+ [Implemented(true), Override("Started")]
+ boolean Started;
+
+ [Implemented(true), Override("TransitioningToState")]
+ uint16 TransitioningToState;
+
+ [Implemented(True), Override("CheckSoftwareIdentity")]
+ uint32 CheckSoftwareIdentity(
+ [IN, Description (
+ "Reference to the SoftwareIdentity to be checked." )]
+ LMI_SoftwareIdentity REF Source,
+ [IN, Description (
+ "Reference to the ManagedElement that the Software "
+ "Identity is going to be installed in (or updated)." )]
+ CIM_ManagedElement REF Target,
+ [IN, Description (
+ "Reference to the Collection to which the Software "
+ "Identity will be added." )]
+ LMI_SystemSoftwareCollection REF Collection,
+ [IN ( false ), OUT, Description (
+ "The parameter describes the characteristics of the "
+ "installation/update that will take place if the "
+ "Source Software Identity is installed: \n"
+ "Target automatic reset: The target element will "
+ "automatically reset once the installation is "
+ "complete. \n"
+ "System automatic reset: The containing system of "
+ "the target ManagedElement (normally a logical "
+ "device or the system itself) will automatically "
+ "reset/reboot once the installation is complete. \n"
+ "Separate target reset required: "
+ "EnabledLogicalElement.RequestStateChange MUST be "
+ "used to reset the target element after the "
+ "SoftwareIdentity is installed. \n"
+ "Separate system reset required: "
+ "EnabledLogicalElement.RequestStateChange MUST be "
+ "used to reset/reboot the containing system of the "
+ "target ManagedElement after the SoftwareIdentity "
+ "is installed. \n"
+ "Manual Reboot Required: The system MUST be "
+ "manually rebooted by the user. \n"
+ "No reboot required : No reboot is required after "
+ "installation. \n"
+ "User Intervention Recomended : It is recommended "
+ "that a user confirm installation of this "
+ "SoftwareIdentity. Inappropriate application MAY "
+ "have serious consequences. \n"
+ "MAY be added to specified collection : The "
+ "SoftwareIndentity MAY be added to specified "
+ "Collection." ),
+ ValueMap { "2", "3", "4", "5", "6", "7", "8", "9",
+ "..", "0x7FFF..0xFFFF" },
+ Values { "Target automatic reset",
+ "System automatic reset",
+ "Separate target reset Required",
+ "Separate system reset Required",
+ "Manual Reboot Required", "No Reboot Required",
+ "User Intervention recommended",
+ "MAY be added to specified Collection",
+ "DMTF Reserved", "Vendor Specific" }]
+ uint16 InstallCharacteristics[]);
+
+ [Implemented(True), Override("InstallFromSoftwareIdentity")]
+ uint32 InstallFromSoftwareIdentity(
+ [IN ( false ), OUT, Description (
+ "Reference to the job (may be null if job completed)."
+ )]
+ LMI_SoftwareInstallationJob REF Job,
+ [IN, Description (
+ "Options to control the install process.\n"
+ "Defer target/system reset : do not automatically "
+ "reset the target/system.\n"
+ "Force installation : Force the installation of the "
+ "same or an older SoftwareIdentity. Install: "
+ "Perform an installation of this software on the "
+ "managed element.\n"
+ "Update: Perform an update of this software on the "
+ "managed element.\n"
+ "Repair: Perform a repair of the installation of "
+ "this software on the managed element by forcing "
+ "all the files required for installing the software "
+ "to be reinstalled.\n"
+ "Reboot: Reboot or reset the system immediately "
+ "after the install or update of this software, if "
+ "the install or the update requires a reboot or reset.\n"
+ "Password: Password will be specified as clear text "
+ "without any encryption for performing the install "
+ "or update.\n"
+ "Uninstall: Uninstall the software on the managed element.\n"
+ "Log: Create a log for the install or update of the software.\n"
+ "SilentMode: Perform the install or update without "
+ "displaying any user interface.\n"
+ "AdministrativeMode: Perform the install or update "
+ "of the software in the administrative mode. "
+ "ScheduleInstallAt: Indicates the time at which "
+ "theinstall or update of the software will occur." ),
+ ValueMap { "2", "3", "4", "5", "6", "7", "8", "9",
+ "10", "11", "12", "13", "..", "32768..65535" },
+ Values { "Defer target/system reset",
+ "Force installation", "Install", "Update", "Repair",
+ "Reboot", "Password", "Uninstall", "Log",
+ "SilentMode", "AdministrativeMode",
+ "ScheduleInstallAt", "DMTF Reserved",
+ "Vendor Specific" },
+ ArrayType ( "Indexed" ),
+ ModelCorrespondence {
+ "CIM_SoftwareInstallationService.InstallOptionsValues[]",
+ "CIM_SoftwareInstallationServiceCapabilities.SupportedInstallOptions[]" }]
+ uint16 InstallOptions[],
+ [IN, Description (
+ "InstallOptionsValues is an array of strings "
+ "providing additional information to InstallOptions "
+ "for the method to install the software. Each entry "
+ "of this array is related to the entry in "
+ "InstallOptions that is located at the same index "
+ "providing additional information for "
+ "InstallOptions. \n"
+ "If the index in InstallOptions has the value "
+ "\"Password \" then a value at the corresponding "
+ "index of InstallOptionValues shall not be NULL. \n"
+ "If the index in InstallOptions has the value "
+ "\"ScheduleInstallAt\" then the value at the "
+ "corresponding index of InstallOptionValues shall "
+ "not be NULL and shall be in the datetime type "
+ "format. \n"
+ "If the index in InstallOptions has the value \"Log "
+ "\" then a value at the corresponding index of "
+ "InstallOptionValues may be NULL. \n"
+ "If the index in InstallOptions has the value "
+ "\"Defer target/system reset\", \"Force "
+ "installation\",\"Install\", \"Update\", \"Repair\" "
+ "or \"Reboot\" then a value at the corresponding "
+ "index of InstallOptionValues shall be NULL." ),
+ ArrayType ( "Indexed" ),
+ ModelCorrespondence {
+ "CIM_SoftwareInstallationService.InstallOptions[]" }]
+ string InstallOptionsValues[],
+ [IN, Description (
+ "Reference to the source of the install." )]
+ LMI_SoftwareIdentity REF Source,
+ [IN, Description (
+ "The installation target. If NULL then the "
+ "SOftwareIdentity will be added to Collection only. "
+ "The underlying implementation is expected to be "
+ "able to obtain any necessary metadata from the "
+ "Software Identity." )]
+ CIM_ManagedElement REF Target,
+ [IN, Description (
+ "Reference to the Collection to which the Software "
+ "Identity SHALL be added. If NULL then the Software "
+ "Identity will not be added to a Collection." )]
+ LMI_SystemSoftwareCollection REF Collection);
+
+ [Implemented(True), Override("InstallFromURI")]
+ uint32 InstallFromURI(
+ [IN ( false ), OUT, Description (
+ "Reference to the job (may be null if job completed)."
+ )]
+ LMI_SoftwareInstallationJob REF Job,
+ [IN, Description (
+ "A URI for the software based on RFC 2079." )]
+ string URI,
+ [IN, Description ( "The installation target." )]
+ CIM_ManagedElement REF Target,
+ [IN, Description (
+ "Options to control the install process. \n"
+ "See the InstallOptions parameter of the "
+ "SoftwareInstallationService.InstallFromSoftwareIdentity "
+ "method for the description of these values." ),
+ ValueMap { "2", "3", "4", "5", "6", "7", "8", "9",
+ "10", "11", "12", "13", "..", "32768..65535" },
+ Values { "Defer target/system reset",
+ "Force installation", "Install", "Update", "Repair",
+ "Reboot", "Password", "Uninstall", "Log",
+ "SilentMode", "AdministrativeMode",
+ "ScheduleInstallAt", "DMTF Reserved",
+ "Vendor Specific" },
+ ArrayType ( "Indexed" ),
+ ModelCorrespondence {
+ "CIM_SoftwareInstallationService.InstallFromURI.InstallOptionsValues[]",
+ "CIM_SoftwareInstallationServiceCapabilities.SupportedInstallOptions[]" }]
+ uint16 InstallOptions[],
+ [IN, Description (
+ "InstallOptionsValues is an array of strings "
+ "providing additionalinformation to InstallOptions "
+ "for the method to install the software. Each entry "
+ "of this array is related to the entry in "
+ "InstallOptions that is located at the same index "
+ "providing additional information for "
+ "InstallOptions. \n"
+ "For further information on the use of "
+ "InstallOptionsValues parameter, see the "
+ "description of the InstallOptionsValues parameter "
+ "of the "
+ "SoftwareInstallationService.InstallFromSoftwareIdentity "
+ "method." ),
+ ArrayType ( "Indexed" ),
+ ModelCorrespondence {
+ "CIM_SoftwareInstallationService.InstallFromByteStream.InstallOptions[]" }]
+ string InstallOptionsValues[]);
+
+};
+
+class LMI_SoftwareInstallationJob : LMI_ConcreteJob {
+
+ [Implemented(true), Override("InstanceID")]
+ string InstanceID;
- [ Description (
- "Returns array of booleans for verification checks. Each value"
- " is either true or false, whether file passed the check or not."
- " True is present also if the check does not apply to the file"
- " (file implicitly passes the check, if check can not be"
- " performed)."
- " PassedFlagsDescriptions returns a description for each field in"
- " this array saying, what it means.") ]
- boolean PassedFlags[];
-
- [ Description (
- "Returns array of descriptions of checks corresponding to PassedFlags"
- " array.")]
- string PassedFlagsDescriptions[];
+ [Implemented(true), Override("Caption")]
+ string Caption;
+
+ [Implemented(true), Override("CommunicationStatus")]
+ uint16 CommunicationStatus;
+
+ [Implemented(true), Override("Description")]
+ string Description;
+
+ [Implemented(true), Override("ErrorCode")]
+ uint16 ErrorCode;
+
+ [Implemented(true), Override("JobStatus")]
+ string JobStatus;
+
+ [Implemented(true), Override("MethodName")]
+ string MethodName;
+
+ [Implemented(true), Override("Priority")]
+ uint32 Priority;
+ [Implemented(true), Override("RecoveryAction")]
+ uint16 RecoveryAction;
+
+};
+
+class LMI_SoftwareMethodResult : LMI_MethodResult {
};
-// ===================================================================
-// Associations
-// ===================================================================
-[ Association ]
-class LMI_SoftwareInstalledPackage : CIM_InstalledSoftwareElement {
-
- [ Override("Software"), Weak, Description (
- "Reference to the installed RPM package.")]
- LMI_SoftwarePackage REF Software;
-
- [ Override("System"), Min(1), Description (
- "Reference to the ComputerSystem hosting a particular"
- " RPM package.")]
- CIM_ComputerSystem REF System;
-
- [ Description (
- "Updates the package to latest version."
- " When any of \"epoch\", \"version\" or \"release\" argument is given"
- ", install only matching available package. Otherwise try"
- " to update to newest package available."
- " If the \"epoch\", \"version\" and \"release\" of package is"
- " already installed, or no newer package is available,"
- " \"Already newest\" value is returned."),
- ValueMap { "0", "1", "2" },
- Values { "Already newest", "Successful installation", "Failed" } ]
- uint16 Update(
- [ IN(true), Description (
- "Specify particular epoch of package to update to."
- " Update to newest, when empty.") ]
- string Epoch,
- [ IN(true), Description (
- "Specify particular version of package to update to."
- " Update to newest, when empty") ]
- string Version,
- [ IN(true), Description (
- "Specify particular release of package to update to."
- " Update to newest, when empty.") ]
- string Release,
- [ IN(false), OUT, Description (
- "The reference to newly installed package, if installation was"
- " successful. Otherwise reference to current package.") ]
- LMI_SoftwareInstalledPackage REF Installed);
-
- [ Description (
- "Verify existence and attributes of files installed from"
- " RPM package. If all files installed exist and their attributes"
- " matches, method returns \"Pass\". \"Not passed\" is returned when"
- " arbitrary file differs in its attributes. And \"Error\" if"
- " verification could not be done."),
- ValueMap { "0", "1", "2" },
- Values { "Pass", "Not passed", "Error" } ]
- uint32 CheckIntegrity(
- [ IN(false), OUT, Description (
- "Array of references to LMI_SoftwareFileCheck, that did not pass"
- " verification.") ]
- LMI_SoftwareFileCheck REF Failed[]);
+[Association]
+class LMI_AssociatedSoftwareJobMethodResult : LMI_AssociatedJobMethodResult {
+
+ [Override("Job")]
+ LMI_SoftwareInstallationJob REF Job;
+
+ [Override("JobParameters")]
+ LMI_SoftwareMethodResult REF JobParameters;
};
-[ Association, Aggregation ]
-class LMI_SoftwarePackageChecks : CIM_SoftwareElementChecks {
+[Association]
+class LMI_OwningSoftwareJobElement : LMI_OwningJobElement {
+
+ [Override("OwningElement")]
+ LMI_SoftwareInstallationService REF OwningElement;
+
+ [Override("OwnedElement")]
+ LMI_SoftwareInstallationJob REF OwnedElement;
+
+};
- [ Override("Element"), Description("The RPM package being checked") ]
- LMI_SoftwarePackage REF Element;
+[Association]
+class LMI_AffectedSoftwareJobElement : LMI_AffectedJobElement {
- [ Override("Check"), Description("The Check for the element.") ]
- LMI_SoftwareFileCheck REF Check;
+ [Override("AffectingElement")]
+ LMI_SoftwareInstallationJob REF AffectingElement;
};
diff --git a/mof/LMI_Software.reg b/mof/LMI_Software.reg
index 1c6f323..1c98fab 100644
--- a/mof/LMI_Software.reg
+++ b/mof/LMI_Software.reg
@@ -1,23 +1,84 @@
-[LMI_SoftwarePackage]
+[LMI_SoftwareIdentity]
provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
location: pyCmpiProvider
- type: instance method
+ type: instance
+ namespace: root/cimv2
+
+[LMI_SystemSoftwareCollection]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance
+ namespace: root/cimv2
+
+[LMI_HostedSoftwareCollection]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
+ namespace: root/cimv2
+
+[LMI_MemberOfSoftwareCollection]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
+ namespace: root/cimv2
+
+[LMI_InstalledSoftwareIdentity]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
+ namespace: root/cimv2
+
+[LMI_SoftwareIdentityResource]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance
+ namespace: root/cimv2
+
+[LMI_ResourceForSoftwareIdentity]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
namespace: root/cimv2
-[LMI_SoftwareInstalledPackage]
+[LMI_HostedSoftwareIdentityResource]
provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
location: pyCmpiProvider
- type: instance association method
+ type: instance association
namespace: root/cimv2
-[LMI_SoftwareFileCheck]
+[LMI_SoftwareInstallationService]
provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
location: pyCmpiProvider
type: instance method
namespace: root/cimv2
-[LMI_SoftwarePackageChecks]
+[LMI_SoftwareInstallationJob]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance method
+ namespace: root/cimv2
+
+[LMI_SoftwareMethodResult]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance
+ namespace: root/cimv2
+
+[LMI_AssociatedSoftwareJobMethodResult]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
+ namespace: root/cimv2
+
+[LMI_OwningSoftwareJobElement]
provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
location: pyCmpiProvider
type: instance association
namespace: root/cimv2
+
+[LMI_AffectedSoftwareJobElement]
+ provider: /usr/lib/python2.7/site-packages/openlmi/software/cimom_entry.py
+ location: pyCmpiProvider
+ type: instance association
+ namespace: root/cimv2
+
diff --git a/openlmi-mof-register b/openlmi-mof-register
index 61e54af..e322664 100755
--- a/openlmi-mof-register
+++ b/openlmi-mof-register
@@ -23,17 +23,29 @@ pegasus_repository="/var/lib/Pegasus/"
function usage()
{
- printf "Usage: $0 [ register | unregister ] <mof> [mof] [...] <reg>\n"
+ printf "Usage: $0 [ --just-mofs ] CMD <mof> [mof] [...] [reg]
+ CMD is one of [ register, unregister ]
+
+ just-mofs option causes that all arguments after CMD will be
+ treated as mof files - no registration file is expected.
+
+ usage with --just-mofs:
+ $0 CMD <mof> [mof] [...]
+ usage without:
+ $0 CMD <mof> [mof] [...] <reg>\n"
+
}
function register()
{
- reg=$1
- shift
+ if [ $JUST_MOFS -eq 0 ]; then
+ reg=$1
+ shift
+ fi
mofs="$@"
if [ $HAS_SFCBD -eq 1 ];
then
- /usr/bin/sfcbstage -r "$reg" "$mofs"
+ /usr/bin/sfcbstage ${reg:+-r} $reg $mofs
/usr/bin/sfcbrepos -f
/usr/bin/systemctl reload-or-try-restart sblim-sfcb.service
fi
@@ -49,19 +61,23 @@ function register()
fi
$CIMMOF -uc $mofs
- if [ -x $(dirname $0)/openlmi-register-pegasus ];
- then
- cat "$reg" | $(dirname $0)/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
- else
- cat "$reg" | /usr/libexec/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
+ if [ $JUST_MOFS = 0 ]; then
+ if [ -x $(dirname $0)/openlmi-register-pegasus ];
+ then
+ cat "$reg" | $(dirname $0)/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
+ else
+ cat "$reg" | /usr/libexec/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
+ fi
fi
fi
}
function unregister()
{
- reg=$1
- shift
+ if [ $JUST_MOFS -eq 0 ]; then
+ reg=$1
+ shift
+ fi
# convert mofs to `basename mof`
declare -a mofs=("$@")
for ((i=0; i<${#mofs[@]}; i++)); do
@@ -70,12 +86,12 @@ function unregister()
if [ $HAS_SFCBD -eq 1 ];
then
- /usr/bin/sfcbunstage -r $(basename "$reg") ${mofs[@]}
+ /usr/bin/sfcbunstage ${reg:+-r} $(basename "$reg") ${mofs[@]}
/usr/bin/sfcbrepos -f
/usr/bin/systemctl reload-or-try-restart sblim-sfcb.service
fi
- if [ $HAS_PEGASUS -eq 1 ];
+ if [ -n "$reg" -a $HAS_PEGASUS -eq 1 ];
then
for provider in $(sed -n 's/ *location: *//p' "$reg" | sort | uniq);
do
@@ -106,6 +122,12 @@ fi
# TODO: check if at least one server is installed
+if [ "$1" = "--just-mofs" ]; then
+ JUST_MOFS=1
+ shift
+else
+ JUST_MOFS=0
+fi
CMD=$1
shift
diff --git a/src/software/cli/software.py b/src/software/cli/software.py
index c92a12d..55c3592 100755
--- a/src/software/cli/software.py
+++ b/src/software/cli/software.py
@@ -19,20 +19,21 @@
# Authors: Michal Minar <miminar@redhat.com>
#
+"""
+Command line tool for simple software management with OpenLMI CIM software
+providers.
+"""
+
import argparse
from collections import defaultdict
-import os
import pywbem
-import re
import socket
-import subprocess
import sys
-import unittest
-re_nevra = re.compile(r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
- r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+from openlmi.software import util
+from openlmi.software.core import SystemSoftwareCollection
-cim_error2text = defaultdict(lambda: "OTHER_ERROR", {
+CIM_ERROR2TEXT = defaultdict(lambda: "OTHER_ERROR", {
1 : "FAILED",
2 : "ACCESS_DENIED",
3 : "INVALID_NAMESPACE",
@@ -53,146 +54,74 @@ cim_error2text = defaultdict(lambda: "OTHER_ERROR", {
})
class NotFound(Exception):
+ """
+ Exception raised, when desired package could not be found.
+ """
def __init__(self, package, details=None):
- msg = 'Package "%s" not installed!'%package
+ msg = 'Package "%s" not installed!' % package
if details is not None:
msg += ' : '+details
Exception.__init__(self, msg)
-HOSTNAME = None
def get_host_name():
- global HOSTNAME
- if HOSTNAME is None:
- HOSTNAME = socket.gethostname()
- return HOSTNAME
-
-def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
"""
- @param with_epoch may be one of:
- "NOT_ZERO" - include epoch only if it's not zero
- "ALWAYS" - include epoch always
- "NEVER" - do not include epoch at all
+ @return computer host name
"""
- estr = ''
- if with_epoch.lower() == "always":
- estr = epoch
- elif with_epoch.lower() == "not_zero":
- if epoch != "0":
- estr = epoch
- if len(estr): estr += ":"
- return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
+ if not hasattr(get_host_name, '_hostname'):
+ get_host_name._hostname = socket.gethostname() #pylint: disable=W0212
+ return get_host_name._hostname #pylint: disable=W0212
def make_pkg_path(name, epoch, ver, rel, arch):
+ """
+ @return instance name for LMI_SoftwareIdentity
+ """
return pywbem.CIMInstanceName(
- classname="LMI_SoftwarePackage", namespace="root/cimv2",
+ classname="LMI_SoftwareIdentity", namespace="root/cimv2",
keybindings={
- "Name" : name,
- "SoftwareElementID" : make_nevra(
- name, epoch, ver, rel, arch, "ALWAYS"),
- "SoftwareElementState" : pywbem.Uint16(2),
- "TargetOperatingSystem" : pywbem.Uint16(36),
- "Version" : ver })
-
-def make_inst_pkg_path(*args):
- op = pywbem.CIMInstanceName(classname="LMI_SoftwareInstalledPackage",
- namespace='root/cimv2')
- system_op = pywbem.CIMInstanceName(
- classname="CIM_ComputerSystem", namespace="root/cimv2",
- keybindings={
- "CreationClassName" : "CIM_ComputerSystem",
- "Name" : get_host_name() })
- op["Software"] = make_pkg_path(*args)
- op["System"] = system_op
- return op
-
-def get_instance_name(package, is_nevra=False, installed=True):
- m = re_nevra.match(package)
- if not m and is_nevra:
- raise ValueError('Expected a valid nevra!.')
- if not m and not installed:
- raise ValueError('You must supply a valid nevra!')
- if not m: # given only a name of package
- # try to enumerate installed packages and find a correct one
- inames = conn.EnumerateInstanceNames(
- ClassName='LMI_SoftwareInstalledPackage',
- namespace='root/cimv2')
- for iname in inames:
- if iname['Software']['Name'] == package:
- break
- else:
- raise NotFound(package)
- else: # nevra given
- try:
- args = [m.group(k) for k in 'name', 'epoch', 'ver', 'rel', 'arch']
- if not args[1]: # epoch not given
- args[1] = '0'
- func = (make_pkg_path, make_inst_pkg_path)[1 if installed else 0]
- inst = conn.GetInstance(InstanceName=func(*args), LocalOnly=False)
- iname = inst.path
- except pywbem.CIMError as e:
- if pywbem.CIM_ERR_NOT_FOUND:
- raise NotFound(package, e.args[1])
- raise
- return iname
-
-def install(conn, nevra):
- iname = get_instance_name(nevra, True, False)
- (rval, oparms) = conn.InvokeMethod(MethodName='Install', ObjectName=iname)
- print (rval, oparms)
-
-def update(conn, package, epoch=None, version=None, release=None):
- iname = get_instance_name(package)
- kwargs = dict(MethodName='Update', ObjectName=iname)
- for name, val in ( ('Epoch', epoch), ('Version', version)
- , ('Release', release)):
- if val is None: continue
- kwargs[name] = str(val)
- (rval, oparms) = conn.InvokeMethod(**kwargs)
- print (rval, oparms)
-
-def remove(conn, package):
- iname = get_instance_name(package)
- conn.DeleteInstance(iname)
-
-def verify(conn, package):
- iname = get_instance_name(package)
- (rval, oparms) = conn.InvokeMethod(MethodName='CheckIntegrity',
- ObjectName=iname)
- if rval == 0:
- print "Passed"
- elif rval == 2:
- print "Error"
- else: # Not Passed
- print "Not Passed:"
- for f in oparms["Failed"]:
- inst = conn.GetInstance(InstanceName=f, LocalOnly=False)
- print " %s"%inst["Name"]
- if not inst['FileExists']:
- print " - does not exist"
- else:
- for arg in ( 'Checksum', 'LinkTarget', 'FileGroupID'
- , 'FileUserID', 'FileMode', 'FileSize'
- , 'FileType', 'LastModificationTime'):
- if inst['Expected'+arg] != inst[arg]:
- print " - %s\t: %s != %s"%(arg,
- inst['Expected'+arg], inst[arg])
-
-def list_installed(conn):
- inames = conn.EnumerateInstanceNames(
- ClassName='LMI_SoftwareInstalledPackage',
- namespace='root/cimv2')
- for nevra in sorted((i['Software']['SoftwareElementID'] for i in inames)):
- print nevra
-
-def list_files(conn, package):
- iname = get_instance_name(package)
- # TODO: find out, why passing role='Check' failes
- inames = conn.ReferenceNames(ObjectName=iname['Software'],
- ResultClass='LMI_SoftwarePackageChecks')
- for i in inames:
- print i['Check']['Name']
-
-if __name__ == '__main__':
+ "InstanceID" : util.make_nevra(
+ name, epoch, ver, rel, arch, "ALWAYS")})
+
+def install(_conn, _nevra):
+ """Install package by nevra."""
+ raise NotImplementedError("Installation is not yet supported!")
+
+def update(_conn, _package, _epoch=None, _version=None, _release=None):
+ """Update to particular evr of package."""
+ raise NotImplementedError("Update of package is not yet supported!")
+
+def remove(_conn, _package):
+ """Remove installed package by its name."""
+ raise NotImplementedError("Removal is not yet supported!")
+
+def verify(_conn, _package):
+ """Verity installed package by its name."""
+ raise NotImplementedError("Verification is not yet supported!")
+
+def list_available(conn):
+ """List nevra strings of available packages."""
+ inames = conn.AssociatorNames(
+ ObjectName=SystemSoftwareCollection.get_path(),
+ ResultClass='LMI_SoftwareIdentity',
+ AssocClass="LMI_MemberOfSoftwareCollection",
+ Role="Collection",
+ ResultRole="Member")
+ for nevra in (i['InstanceID'] for i in inames):
+ print nevra[len("LMI:SoftwareIdentity:"):]
+
+def list_installed(_conn):
+ """List nevra strings of installed packages."""
+ raise NotImplementedError(
+ "Listing of installed packages is not yet supported!")
+
+def list_files(_conn, _package):
+ """List files of installed package."""
+ raise NotImplementedError("Listing of package files is not yet supported!")
+
+def parse_args():
+ """
+ Parse command line arguments and handle related errors.
+ @return Namespace object
+ """
parser = argparse.ArgumentParser(prog='software',
description=("CLI tool for testing OpenLMI software providers."
" With this tool you are able to install, update,"
@@ -201,12 +130,12 @@ if __name__ == '__main__':
parser.add_argument('--url',
help="Specify schema, hostname and port of broker in one argument."
" For user and password, please use provided options.")
- parser.add_argument('-p', '--port', type=int, default=5988,
- help="Port of cimom broker.")
+ parser.add_argument('-p', '--port', type=int, default=5989,
+ help="Port of cimom broker. Default is %(default)d.")
parser.add_argument('-h', '--hostname',
default='localhost', help="Hostname of cimom broker.")
parser.add_argument('-s', '--schema', choices=('http', 'https'),
- default='http')
+ default='https', help="Schema part of url (default is %(default)s)")
parser.add_argument('-u', '--user', default='',
help="Under which user to authenticate.")
parser.add_argument('--password', default='',
@@ -247,44 +176,65 @@ if __name__ == '__main__':
parse_verify.set_defaults(command='verify')
parse_list = subpars.add_parser('list',
- help="List installed packages.")
+ help="List various information depending on next argument."
+ " See \"%(prog)s list --help\".")
parse_list.set_defaults(command='list')
- parse_files = subpars.add_parser('list-files',
- help="List files of installed package.")
- parse_files.add_argument('package',
+ list_subpars = parse_list.add_subparsers(help="What should be listed.")
+ parse_list_available = list_subpars.add_parser("available",
+ help="List available packages.")
+ parse_list_available.set_defaults(list_kind='available')
+ parse_list_installed = list_subpars.add_parser("installed",
+ help="List installed packages.")
+ parse_list_installed.set_defaults(list_kind='installed')
+ parse_list_files = list_subpars.add_parser("files",
+ help="List files of installed package.")
+ parse_list_files.set_defaults(list_kind='files')
+ parse_list_files.add_argument('package',
help="Name or nevra of installed package.")
- parse_files.set_defaults(command='list-files')
args = parser.parse_args()
- if args.url is not None:
- url = args.url
- else:
- url = '%s://%s:%d' % (args.schema, args.hostname, args.port)
- HOSTNAME = re.match('^https?://([^:]+)', url).group(1)
+ if args.url is None:
+ args.url = '%s://%s:%d' % (args.schema, args.hostname, args.port)
+
+ return args
+
+def main(args):
+ """
+ Main functionality.
+ @return return code of script
+ """
auth = (args.user, args.password)
if args.debug:
- sys.stderr.write('url:\t%s\n'%url)
- conn = pywbem.WBEMConnection(url, auth)
-
- func, attrs = \
- { 'install' : (install, ('nevra',))
- , 'update' : (update, ('package', 'epoch', 'version', 'release'))
- , 'remove' : (remove, ('package',))
- , 'verify' : (verify, ('package',))
- , 'list' : (list_installed, tuple())
- , 'list-files' : (list_files, ('package',))
- }[args.command]
+ sys.stderr.write('url:\t%s\n'%args.url)
+ conn = pywbem.WBEMConnection(args.url, auth)
+
+ if args.command == 'list':
+ func, attrs = \
+ { 'available' : (list_available, tuple())
+ , 'installed' : (list_installed, tuple())
+ , 'files' : (list_files, ('package'))
+ }[args.list_kind]
+ else:
+ func, attrs = \
+ { 'install' : (install, ('nevra',))
+ , 'update' : (update, ('package', 'epoch', 'version', 'release'))
+ , 'remove' : (remove, ('package',))
+ , 'verify' : (verify, ('package',))
+ }[args.command]
try:
func(conn, *(getattr(args, a) for a in attrs))
- sys.exit(0)
- except pywbem.CIMError as e:
+ return 0
+ except pywbem.CIMError as exc:
sys.stderr.write('Failed: %d (%s) - %s\n' % (
- e.args[0], cim_error2text[e.args[0]],
- e.args[1].replace('<br>', '\n')))
- sys.exit(1)
- except NotFound as e:
- sys.stderr.write(str(e) + '\n')
- sys.exit(1)
+ exc.args[0], CIM_ERROR2TEXT[exc.args[0]],
+ exc.args[1].replace('<br>', '\n')))
+ return 1
+ except NotFound as exc:
+ sys.stderr.write(str(exc) + '\n')
+ return 1
+if __name__ == '__main__':
+ ARGS = parse_args()
+ sys.exit(main(ARGS))
diff --git a/src/software/openlmi/software/LMI_AffectedSoftwareJobElement.py b/src/software/openlmi/software/LMI_AffectedSoftwareJobElement.py
new file mode 100644
index 0000000..ec1c0ab
--- /dev/null
+++ b/src/software/openlmi/software/LMI_AffectedSoftwareJobElement.py
@@ -0,0 +1,235 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_AffectedSoftwareJobElement
+
+Instruments the CIM class LMI_AffectedSoftwareJobElement
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import AffectedSoftwareJobElement
+from openlmi.software.core import InstallationJob
+from openlmi.software.yumdb import YumDB
+
+class LMI_AffectedSoftwareJobElement(CIMProvider2):
+ """Instrument the CIM class LMI_AffectedSoftwareJobElement
+
+ AffectedJobElement represents an association between a Job and the
+ ManagedElement(s) that may be affected by its execution. It may not be
+ feasible for the Job to describe all of the affected elements. The
+ main purpose of this association is to provide information when a Job
+ requires exclusive use of the 'affected' ManagedElment(s) or when
+ describing that side effects may result.
+ """
+
+ def __init__(self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = AffectedSoftwareJobElement.Values
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ (job, affected) = AffectedSoftwareJobElement.check_path(env, model.path)
+ model['AffectingElement'] = InstallationJob.job2model(job)
+ if affected.classname == "LMI_SoftwareIdentity":
+ _, effects, descriptions = AffectedSoftwareJobElement. \
+ job2affected_software_identity(job)
+ model["AffectedElement"] = affected
+ model["ElementEffects"] = effects
+ model["OtherElementEffectsDescriptions"] = descriptions
+ elif affected.classname == "LMI_ComputerSystem":
+ AffectedSoftwareJobElement.fill_model_computer_system(
+ model, env, keys_only=False)
+ elif affected.classname == "LMI_ComputerSystem":
+ AffectedSoftwareJobElement.fill_model_system_collection(
+ model, keys_only=False)
+ else:
+ cmpi_logging.logger.error("Unhandled classname: %s",
+ affected.classname)
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ model.path.update({'AffectedElement': None, 'AffectingElement': None})
+ for job in YumDB.get_instance().get_job_list():
+ for mdl in AffectedSoftwareJobElement.generate_models_from_job(
+ job, keys_only=keys_only, model=model):
+ yield mdl
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_ManagedElement') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_Job'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/openlmi/software/LMI_AssociatedSoftwareJobMethodResult.py b/src/software/openlmi/software/LMI_AssociatedSoftwareJobMethodResult.py
new file mode 100644
index 0000000..ace38b5
--- /dev/null
+++ b/src/software/openlmi/software/LMI_AssociatedSoftwareJobMethodResult.py
@@ -0,0 +1,248 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_AssociatedSoftwareJobMethodResult
+
+Instruments the CIM class LMI_AssociatedSoftwareJobMethodResult
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import generate_references
+from openlmi.software.core import InstallationJob
+from openlmi.software.core import MethodResult
+from openlmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_job_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ job = InstallationJob.object_path2job(object_name)
+
+ model["Job"] = InstallationJob.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_result_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ job = MethodResult.object_path2job(object_name)
+
+ model["Job"] = InstallationJob.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ yield model
+
+class LMI_AssociatedSoftwareJobMethodResult(CIMProvider2):
+ """Instrument the CIM class LMI_AssociatedSoftwareJobMethodResult
+
+ AssociatedJobMethodResult represents an association between a
+ ConcreteJob and the MethodResult expressing the parameters for the Job
+ when the job was created by side-effect of the execution of an
+ extrinsic method.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ job = InstallationJob.object_path2job(model['Job'])
+ jobid = MethodResult.object_path2jobid(model['JobParameters'])
+ if job.jobid != jobid:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Job id of Job(%d) does not match JobParameters(%d)." %
+ (job.jobid, jobid))
+ model["Job"] = InstallationJob.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ model.path.update({'Job': None, 'JobParameters': None})
+
+ for job in YumDB.get_instance().get_job_list():
+ model['Job'] = InstallationJob.job2model(job)
+ model['JobParameters'] = MethodResult.job2model(job)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ handlers = [
+ ("Job", "LMI_SoftwareInstallationJob", generate_job_referents),
+ ("JobParameters", "LMI_SoftwareMethodResult",
+ generate_result_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/openlmi/software/LMI_HostedSoftwareCollection.py b/src/software/openlmi/software/LMI_HostedSoftwareCollection.py
new file mode 100644
index 0000000..113ce7c
--- /dev/null
+++ b/src/software/openlmi/software/LMI_HostedSoftwareCollection.py
@@ -0,0 +1,238 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_HostedSoftwareCollection
+
+Instruments the CIM class LMI_HostedSoftwareCollection
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import ComputerSystem, SystemCollection
+
+class LMI_HostedSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_HostedSoftwareCollection
+
+ HostedSoftwareCollection defines a SystemSpecificCollection in the context of a
+ scoping System. It represents a Collection that has meaning only in
+ the context of a System, a Collection whose elements are restricted by
+ the definition of the System, or both of these types of Collections.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ for key_prop in ("Antecedent", "Dependent"):
+ if not key_prop in model:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing Antecedent key property!")
+ ComputerSystem.check_path_property(env, model, "Antecedent")
+ SystemCollection.check_path_property(env, model, "Dependent")
+
+ model.path.update({"Antecedent":None, "Dependent":None})
+
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = SystemCollection.get_path()
+
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'Dependent': None, 'Antecedent': None})
+
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = SystemCollection.get_path()
+
+ yield model
+
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ ch = env.get_cimom_handle()
+ # If you want to get references for free, implemented in terms
+ # of enum_instances, just leave the code below unaltered.
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_SystemSpecificCollection') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_System'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
+
diff --git a/src/software/openlmi/software/LMI_SoftwarePackageChecks.py b/src/software/openlmi/software/LMI_HostedSoftwareIdentityResource.py
index 5b898d4..d6e07b0 100644
--- a/src/software/openlmi/software/LMI_SoftwarePackageChecks.py
+++ b/src/software/openlmi/software/LMI_HostedSoftwareIdentityResource.py
@@ -19,9 +19,9 @@
# Authors: Michal Minar <miminar@redhat.com>
#
-"""Python Provider for LMI_SoftwarePackageChecks
+"""Python Provider for LMI_HostedSoftwareIdentityResource
-Instruments the CIM class LMI_SoftwarePackageChecks
+Instruments the CIM class LMI_HostedSoftwareIdentityResource
"""
@@ -29,17 +29,47 @@ import pywbem
from pywbem.cim_provider2 import CIMProvider2
from openlmi.common import cmpi_logging
-from openlmi.software.core import (SoftwarePackage, SoftwareFileCheck)
+from openlmi.software.core import generate_references
+from openlmi.software.core import ComputerSystem
+from openlmi.software.core import IdentityResource
from openlmi.software.yumdb import YumDB
-class LMI_SoftwarePackageChecks(CIMProvider2):
- """Instrument the CIM class LMI_SoftwarePackageChecks
-
- This association ties a SoftwareElement to a specific Check to validate
- its state or its movement to the next state. Note that
- SoftwareElements in a running state cannot transition to another
- state. Therefore, the value of the Phase property is restricted to 0
- ("In-State") for SoftwareElements in the running state.
+@cmpi_logging.trace_function
+def generate_system_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ ComputerSystem.check_path(env, object_name, "Antecedent")
+ repomodel = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityResource',
+ namespace='root/cimv2',
+ host=model.path.host)
+ model["Antecedent"] = ComputerSystem.get_path()
+ for repo in YumDB.get_instance().get_repository_list('all'):
+ model["Dependent"] = IdentityResource.repo2model(repo, model=repomodel)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_repository_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ repo = IdentityResource.object_path2repo(
+ env, object_name, kind='all')
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = IdentityResource.repo2model(repo)
+ yield model
+
+class LMI_HostedSoftwareIdentityResource(CIMProvider2):
+ """Instrument the CIM class LMI_HostedSoftwareIdentityResource
+
+ CIM_HostedAccessPoint is an association between a Service AccessPoint
+ and the System on which it is provided. The cardinality of this
+ association is one-to-many and is weak with respect to the System.
+ Each System can host many ServiceAccessPoints. Heuristic: If the
+ implementation of the ServiceAccessPoint is modeled, it must be
+ implemented by a Device or SoftwareFeature that is part of the System
+ that is hosting the ServiceAccessPoint.
"""
@@ -70,18 +100,20 @@ class LMI_SoftwarePackageChecks(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
- if not "Check" in model:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Check property.")
- if not "Element" in model:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Element property.")
-
- pkg_info, pkg_check, pkg_file = \
- SoftwareFileCheck.object_path2pkg_file(model['Check'])
- model['Check'] = SoftwareFileCheck.filecheck2model(
- pkg_info, pkg_check, pkg_file.path)
- model['Element'] = SoftwarePackage.pkg2model(pkg_info)
+ ComputerSystem.check_path_property(env, model, "Antecedent")
+ if not "Name" in model['Dependent']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing key property "Name" in Dependent!')
+
+ with YumDB.get_instance() as ydb:
+ repoid = model["Dependent"]["Name"]
+ repos = ydb.filter_repositories('all', repoid=repoid)
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Unknown repository "%s".' % repoid)
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = IdentityResource.repo2model(repos[0])
+
return model
@cmpi_logging.trace_method
@@ -107,8 +139,21 @@ class LMI_SoftwarePackageChecks(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
- "Enumeration of instances is not supported.")
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'Dependent': None, 'Antecedent': None})
+
+ repolist = YumDB.get_instance().get_repository_list('all')
+ model["Antecedent"] = ComputerSystem.get_path()
+ repomodel = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityResource',
+ namespace='root/cimv2')
+ for repo in repolist:
+ model["Dependent"] = IdentityResource.repo2model(
+ repo, model=repomodel)
+ yield model
@cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
@@ -137,6 +182,7 @@ class LMI_SoftwarePackageChecks(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
+
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
@cmpi_logging.trace_method
@@ -222,42 +268,13 @@ class LMI_SoftwarePackageChecks(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
- cimhandle = env.get_cimom_handle()
- # Prime model.path with knowledge of the keys, so key values on
- # the CIMInstanceName (model.path) will automatically be set when
- # we set property values on the model.
- model.path.update({'Check': None, 'Element': None})
-
- with YumDB.getInstance() as ydb:
- if ( (not role or role.lower() == 'element')
- and cimhandle.is_subclass(object_name.namespace,
- sub=object_name.classname,
- super='LMI_SoftwarePackage')):
- filecheck_model = pywbem.CIMInstanceName(
- classname='LMI_SoftwareFileCheck',
- namespace="root/cimv2",
- host=model.path.host)
- pkg_info = SoftwarePackage.object_path2pkg(object_name,
- kind="installed")
- model['Element'] = SoftwarePackage.pkg2model(pkg_info)
-
- pkg_check = ydb.check_package(pkg_info)
- for file_name in pkg_check:
- model['Check'] = SoftwareFileCheck.filecheck2model(
- pkg_info, pkg_check, file_name,
- model=filecheck_model)
- yield model
-
- if ( (not role or role.lower() == 'check')
- and cimhandle.is_subclass(object_name.namespace,
- sub=object_name.classname,
- super='LMI_SoftwareFileCheck')):
- pkg_info, pkg_check, pkg_file = \
- SoftwareFileCheck.object_path2pkg_file(object_name)
- model['Check'] = SoftwareFileCheck.filecheck2model(
- pkg_info, pkg_check, pkg_file.path)
-
- model['Element'] = SoftwarePackage.pkg2model(pkg_info)
- yield model
+ handlers = [
+ ("Antecedent", "CIM_ComputerSystem" , generate_system_referents),
+ ("Dependent", "LMI_SoftwareIdentityResource",
+ generate_repository_referents)
+ ]
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
diff --git a/src/software/openlmi/software/LMI_InstalledSoftwareIdentity.py b/src/software/openlmi/software/LMI_InstalledSoftwareIdentity.py
new file mode 100644
index 0000000..ab4fbea
--- /dev/null
+++ b/src/software/openlmi/software/LMI_InstalledSoftwareIdentity.py
@@ -0,0 +1,309 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_InstalledSoftwareIdentity
+
+Instruments the CIM class LMI_InstalledSoftwareIdentity
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import generate_references
+from openlmi.software.core import ComputerSystem
+from openlmi.software.core import Identity
+from openlmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_system_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ ComputerSystem.check_path(env, object_name, "object_name")
+
+ pkg_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentity',
+ namespace="root/cimv2",
+ host=model.path.host)
+ model["System"] = ComputerSystem.get_path()
+ with YumDB.get_instance() as ydb:
+ for pkg_info in ydb.get_package_list('installed', sort=True):
+ model["InstalledSoftware"] = Identity.pkg2model(
+ pkg_info, model=pkg_model)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_package_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ pkg_info = Identity.object_path2pkg(object_name, kind="installed")
+ model['InstalledSoftware'] = Identity.pkg2model(pkg_info)
+ model["System"] = ComputerSystem.get_path()
+ yield model
+
+class LMI_InstalledSoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_InstalledSoftwareIdentity
+
+ The InstalledSoftwareIdentity association identifies the System on
+ which a SoftwareIdentity is installed. This class is a corollary to
+ InstalledSoftwareElement, but deals with the asset aspects of software
+ (as indicated by SoftwareIdentity), versus the deployment aspects (as
+ indicated by SoftwareElement).
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ ComputerSystem.check_path_property(env, model, 'System')
+
+ if not isinstance(model['InstalledSoftware'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for InstalledSoftware!")
+
+ model["System"] = model.path["System"] = ComputerSystem.get_path()
+ with YumDB.get_instance():
+ pkg_info = Identity.object_path2pkg(
+ model['InstalledSoftware'], kind='installed')
+ model['InstalledSoftware'] = Identity.pkg2model(pkg_info)
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'InstalledSoftware': None, 'System': None})
+
+ model['System'] = ComputerSystem.get_path()
+ inst_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ with YumDB.get_instance() as yb:
+ pl = yb.get_package_list('installed', sort=True)
+ for pkg in pl:
+ model['InstalledSoftware'] = Identity.pkg2model(
+ pkg, model=inst_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+
+ if modify_existing:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
+ "Installed package can not be modified.")
+
+ if not "InstalledSoftware" in instance:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing InstalledSoftware property.")
+ if not "System" in instance:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing System property.")
+
+ ComputerSystem.check_path_property(env, instance, 'System')
+ with YumDB.get_instance() as ydb:
+ pkg_info = Identity.object_path2pkg(
+ instance["InstalledSoftware"], kind="all")
+ if pkg_info.installed:
+ raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
+ 'Package "%s" is already installed.' % pkg_info)
+ cmpi_logging.logger.info('installing package "%s"' % pkg_info)
+ installed = ydb.install_package(pkg_info)
+ cmpi_logging.logger.info('package "%s" installed' % pkg_info)
+ instance["InstalledSoftware"] = Identity.pkg2model(installed)
+ return instance
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ if not "InstalledSoftware" in instance_name:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing InstalledSoftware property.")
+ if not "System" in instance_name:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing System property.")
+ ComputerSystem.check_path_property(env, instance_name, 'System')
+
+ with YumDB.get_instance() as ydb:
+ pkg_info = Identity.object_path2pkg(
+ instance_name["InstalledSoftware"], kind="installed")
+ cmpi_logging.logger.info('removing package "%s"' % pkg_info)
+ ydb.remove_package(pkg_info)
+ cmpi_logging.logger.info('package "%s" removed' % pkg_info)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ handlers = [
+ ("System", "CIM_ComputerSystem", generate_system_referents),
+ ("InstalledSoftware", "LMI_SoftwareIdentity",
+ generate_package_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/openlmi/software/LMI_MemberOfSoftwareCollection.py b/src/software/openlmi/software/LMI_MemberOfSoftwareCollection.py
new file mode 100644
index 0000000..1868efa
--- /dev/null
+++ b/src/software/openlmi/software/LMI_MemberOfSoftwareCollection.py
@@ -0,0 +1,280 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_MemberOfSoftwareCollection
+
+Instruments the CIM class LMI_MemberOfSoftwareCollection
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import generate_references
+from openlmi.software.core import SystemCollection
+from openlmi.software.core import Identity
+from openlmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_collection_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ SystemCollection.check_path(env, object_name, "collection")
+ pkg_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentity',
+ namespace="root/cimv2",
+ host=model.path.host)
+ model["Collection"] = SystemCollection.get_path()
+ with YumDB.get_instance() as ydb:
+ for pkg_info in ydb.get_package_list('available',
+ allow_duplicates=True, sort=True):
+ model["Member"] = Identity.pkg2model(pkg_info, model=pkg_model)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_member_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ try:
+ pkg_info = Identity.object_path2pkg(object_name, kind="available")
+ model['Member'] = Identity.pkg2model(pkg_info)
+ model["Collection"] = SystemCollection.get_path()
+ except pywbem.CIMError as exc:
+ if exc.args[0] == pywbem.CIM_ERR_NOT_FOUND:
+ msg = "Could not find requested package%s."
+ if "InstanceID" in object_name:
+ msg = msg % (' with InstanceID="%s"'%object_name["InstanceID"])
+ else:
+ msg = msg % ""
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, msg)
+ else:
+ raise
+ yield model
+
+class LMI_MemberOfSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_MemberOfSoftwareCollection
+
+ LMI_MemberOfSoftwareCollection is an aggregation used to establish
+ membership of ManagedElements in a Collection.
+ """
+
+ def __init__(self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ SystemCollection.check_path_property(env, model, "Collection")
+
+ if not 'Member' in model:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing "Member" key property!')
+ if not isinstance(model['Member'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for Member!")
+
+ model['Collection'] = SystemCollection.get_path()
+ with YumDB.get_instance():
+ pkg_info = Identity.object_path2pkg(model['Member'], 'available')
+ model['Member'] = Identity.pkg2model(pkg_info)
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'Member': None, 'Collection': None})
+
+ model['Collection'] = SystemCollection.get_path()
+ member_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ with YumDB.get_instance() as yb:
+ pl = yb.get_package_list('available',
+ allow_duplicates=True,
+ sort=True)
+ for pkg in pl:
+ model['Member'] = Identity.pkg2model(pkg, model=member_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ handlers = [
+ ("Collection", "LMI_SystemSoftwareCollection",
+ generate_collection_referents),
+ ("Member", "LMI_SoftwareIdentity", generate_member_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/openlmi/software/LMI_OwningSoftwareJobElement.py b/src/software/openlmi/software/LMI_OwningSoftwareJobElement.py
new file mode 100644
index 0000000..8768c0e
--- /dev/null
+++ b/src/software/openlmi/software/LMI_OwningSoftwareJobElement.py
@@ -0,0 +1,223 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_OwningSoftwareJobElement
+
+Instruments the CIM class LMI_OwningSoftwareJobElement
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import InstallationService
+from openlmi.software.core import InstallationJob
+from openlmi.software.yumdb import YumDB
+
+class LMI_OwningSoftwareJobElement(CIMProvider2):
+ """Instrument the CIM class LMI_OwningSoftwareJobElement
+
+ OwningJobElement represents an association between a Job and the
+ ManagedElement responsible for the creation of the Job. This
+ association may not be possible, given that the execution of jobs can
+ move between systems and that the lifecycle of the creating entity may
+ not persist for the total duration of the job. However, this can be
+ very useful information when available. This association defines a
+ more specific 'owner' than is provided by the CIM_Job.Owner string.
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ InstallationService.check_path_property(env, model, "OwningElement")
+ model['OwningElement'] = InstallationService.get_path()
+ job = InstallationJob.object_path2job(model['OwnedElement'])
+ model['OwnedElement'] = InstallationJob.job2model(job)
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ model.path.update({'OwningElement': None, 'OwnedElement': None})
+ model['OwningElement'] = InstallationService.get_path()
+ for job in YumDB.get_instance().get_job_list():
+ model['OwnedElement'] = InstallationJob.job2model(job)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationService') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationJob'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/openlmi/software/LMI_ResourceForSoftwareIdentity.py b/src/software/openlmi/software/LMI_ResourceForSoftwareIdentity.py
new file mode 100644
index 0000000..42ef7dc
--- /dev/null
+++ b/src/software/openlmi/software/LMI_ResourceForSoftwareIdentity.py
@@ -0,0 +1,317 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_ResourceForSoftwareIdentity
+
+Instruments the CIM class LMI_ResourceForSoftwareIdentity
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import generate_references
+from openlmi.software.core import Identity
+from openlmi.software.core import IdentityResource
+from openlmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_package_referents(_env, object_name, model, _keys_only):
+ """
+ Generates models of repositories holding package represented
+ by object_name.
+ """
+ with YumDB.get_instance() as ydb:
+ pkg_infos = Identity.object_path2pkg(
+ object_name, 'available', return_all=True)
+ for pkg_info in pkg_infos:
+ repos = ydb.filter_repositories('enabled', repoid=pkg_info.repoid)
+ model['ManagedElement'] = Identity.pkg2model(pkg_info)
+ for repo in repos:
+ model["AvailableSAP"] = \
+ IdentityResource.repo2model(repo)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_repository_referents(env, object_name, model, _keys_only):
+ """
+ Generates models of software identities contained in repository
+ represented by object_name.
+ """
+ with YumDB.get_instance() as ydb:
+ repo = IdentityResource.object_path2repo(
+ env, object_name, 'all')
+ pkglist = ydb.get_package_list('available',
+ allow_duplicates=True, sort=True,
+ include_repos=repo.repoid,
+ exclude_repos='*')
+ model['AvailableSAP'] = IdentityResource.repo2model(repo)
+ pkg_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ for pkg_info in pkglist:
+ model["ManagedElement"] = Identity.pkg2model(
+ pkg_info, model=pkg_model)
+ yield model
+
+class LMI_ResourceForSoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_ResourceForSoftwareIdentity
+
+ CIM_SAPAvailableForElement conveys the semantics of a Service Access
+ Point that is available for a ManagedElement. When
+ CIM_SAPAvailableForElement is not instantiated, then the SAP is
+ assumed to be generally available. If instantiated, the SAP is
+ available only for the associated ManagedElements. For example, a
+ device might provide management access through a URL. This association
+ allows the URL to be advertised for the device.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ if not isinstance(model['ManagedElement'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for ManagedElement!")
+ if not isinstance(model['AvailableSAP'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for AvailableSAP!")
+ if not "Name" in model['AvailableSAP']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing key property "Name" in AvailableSAP!')
+
+ with YumDB.get_instance() as ydb:
+ repoid = model["AvailableSAP"]["Name"]
+ pkg_info = Identity.object_path2pkg(
+ model["ManagedElement"],
+ kind='available',
+ include_repos=repoid,
+ exclude_repos='*',
+ repoid=repoid)
+ repos = ydb.filter_repositories('all', repoid=repoid)
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Unknown repository "%s".' % repoid)
+ repo = repos[0]
+ repo = IdentityResource.object_path2repo(
+ env, model["AvailableSAP"], 'all')
+
+ model["AvailableSAP"] = IdentityResource.repo2model(repo)
+ model["ManagedElement"] = Identity.pkg2model(pkg_info)
+
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'ManagedElement': None, 'AvailableSAP': None})
+
+ elem_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ sap_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentityResource",
+ namespace="root/cimv2")
+ # maps repoid to instance name
+ with YumDB.get_instance() as ydb:
+ pl = ydb.get_package_list('available',
+ allow_duplicates=True,
+ sort=True)
+ repos = dict( (r.repoid, r)
+ for r in ydb.get_repository_list('enabled'))
+ for pkg in pl:
+ try:
+ repo = repos[pkg.repo]
+ except KeyError:
+ cmpi_logging.logger.error('unknown or disabled repository'
+ ' "%s" for package "%s"' % (pkg, pkg.repo))
+ model["AvailableSAP"] = IdentityResource.repo2model(
+ repo, model=sap_model)
+ model["ManagedElement"] = Identity.pkg2model(
+ pkg, model=elem_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ handlers = [
+ ("AvailableSAP", "LMI_SoftwareIdentityResource",
+ generate_repository_referents),
+ ("ManagedElement", "LMI_SoftwareIdentity",
+ generate_package_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/openlmi/software/LMI_SoftwareFileCheck.py b/src/software/openlmi/software/LMI_SoftwareFileCheck.py
deleted file mode 100644
index b3a767d..0000000
--- a/src/software/openlmi/software/LMI_SoftwareFileCheck.py
+++ /dev/null
@@ -1,247 +0,0 @@
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""Python Provider for LMI_SoftwareFileCheck
-
-Instruments the CIM class LMI_SoftwareFileCheck
-
-"""
-
-import pywbem
-from pywbem.cim_provider2 import CIMProvider2
-
-from openlmi.common import cmpi_logging
-from openlmi.software.core import SoftwareFileCheck
-from openlmi.software.yumdb import YumDB
-
-class LMI_SoftwareFileCheck(CIMProvider2):
- """Instrument the CIM class LMI_SoftwareFileCheck
-
- Identifies a file contained by RPM package. It's located in directory
- identified in FileName. The Invoke methods check for file existence
- and whether its attributes match those given by RPM package.
-
- """
-
- def __init__(self, _env):
- cmpi_logging.logger.debug('Initializing provider %s from %s' \
- % (self.__class__.__name__, __file__))
-
- @cmpi_logging.trace_method
- def get_instance(self, env, model):
- """Return an instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- model -- A template of the pywbem.CIMInstance to be returned. The
- key properties are set on this instance to correspond to the
- instanceName that was requested. The properties of the model
- are already filtered according to the PropertyList from the
- request. Only properties present in the model need to be
- given values. If you prefer, you can set all of the
- values, and the instance will be filtered for you.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
- Instance does not exist in the specified namespace)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- with YumDB.getInstance():
- pkg_info, pkg_check, pkg_file = \
- SoftwareFileCheck.object_path2pkg_file(model.path)
- return SoftwareFileCheck.filecheck2model(
- pkg_info, pkg_check, pkg_file.path, keys_only=False)
-
- @cmpi_logging.trace_method
- def enum_instances(self, env, model, keys_only):
- """Enumerate instances.
-
- The WBEM operations EnumerateInstances and EnumerateInstanceNames
- are both mapped to this method.
- This method is a python generator
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- model -- A template of the pywbem.CIMInstances to be generated.
- The properties of the model are already filtered according to
- the PropertyList from the request. Only properties present in
- the model need to be given values. If you prefer, you can
- always set all of the values, and the instance will be filtered
- for you.
- keys_only -- A boolean. True if only the key properties should be
- set on the generated instances.
-
- Possible Errors:
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- # this won't be supported because of enormous amount of data
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
-
- @cmpi_logging.trace_method
- def set_instance(self, env, instance, modify_existing):
- """Return a newly created or modified instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- instance -- The new pywbem.CIMInstance. If modifying an existing
- instance, the properties on this instance have been filtered by
- the PropertyList from the request.
- modify_existing -- True if ModifyInstance, False if CreateInstance
-
- Return the new instance. The keys must be set on the new instance.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_NOT_SUPPORTED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
- valid if modify_existing is False, indicating that the operation
- was CreateInstance)
- CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
- if modify_existing is True, indicating that the operation
- was ModifyInstance)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
-
- @cmpi_logging.trace_method
- def delete_instance(self, env, instance_name):
- """Delete an instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- instance_name -- A pywbem.CIMInstanceName specifying the instance
- to delete.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_NOT_SUPPORTED
- CIM_ERR_INVALID_NAMESPACE
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
- namespace)
- CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
- Instance does not exist in the specified namespace)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
-
- @cmpi_logging.trace_method
- def cim_method_invoke(self, env, object_name):
- """Implements LMI_SoftwareFileCheck.Invoke()
-
- The Invoke method evaluates this Check. The details of the
- evaluation are described by the specific subclasses of CIM_Check.
- When the SoftwareElement being checked is already installed, the
- CIM_InstalledSoftwareElement association identifies the
- CIM_ComputerSystem in whose context the Invoke is executed. If
- this association is not in place, then the InvokeOnSystem method
- should be used - since it identifies the TargetSystem as an input
- parameter of the method. \nThe results of the Invoke method are
- based on the return value. A zero is returned if the condition is
- satisfied. A one is returned if the method is not supported. Any
- other value indicates the condition is not satisfied.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method Invoke()
- should be invoked.
-
- Returns a two-tuple containing the return value (type pywbem.Uint32)
- and a list of CIMParameter objects representing the output parameters
-
- Output parameters: none
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
- unrecognized or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
- exist in the specified namespace)
- CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
- the invocation request)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- with YumDB.getInstance():
- _, pkg_check, pkg_file = \
- SoftwareFileCheck.object_path2pkg_file(object_name)
- sfc = SoftwareFileCheck.test_file(
- pkg_check.file_checksum_type, pkg_file)
- out_params = []
- ret = 0 if SoftwareFileCheck.filecheck_passed(sfc) else 2
- return (pywbem.Uint32(ret), out_params)
-
- @cmpi_logging.trace_method
- def cim_method_invokeonsystem(self, env, object_name,
- param_targetsystem=None):
- """Implements LMI_SoftwareFileCheck.InvokeOnSystem()
-
- The InvokeOnSystem method evaluates this Check. The details of the
- evaluation are described by the specific subclasses of CIM_Check.
- The method\'s TargetSystem input parameter specifies the
- ComputerSystem in whose context the method is invoked. \nThe
- results of the InvokeOnSystem method are based on the return
- value. A zero is returned if the condition is satisfied. A one is
- returned if the method is not supported. Any other value indicates
- the condition is not satisfied.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method InvokeOnSystem()
- should be invoked.
- param_targetsystem -- The input parameter TargetSystem (
- type REF (pywbem.CIMInstanceName(
- classname='CIM_ComputerSystem', ...))
- Reference to ComputerSystem in whose context the method is to
- be invoked.
-
-
- Returns a two-tuple containing the return value (type pywbem.Uint32)
- and a list of CIMParameter objects representing the output parameters
-
- Output parameters: none
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
- unrecognized or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
- exist in the specified namespace)
- CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
- the invocation request)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- # TODO do something
- raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
-
diff --git a/src/software/openlmi/software/LMI_SoftwareIdentity.py b/src/software/openlmi/software/LMI_SoftwareIdentity.py
new file mode 100644
index 0000000..62c9875
--- /dev/null
+++ b/src/software/openlmi/software/LMI_SoftwareIdentity.py
@@ -0,0 +1,188 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareIdentity
+
+Instruments the CIM class LMI_SoftwareIdentity
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import Identity
+from openlmi.software.yumdb import YumDB
+
+class LMI_SoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentity
+
+ SoftwareIdentity provides descriptive information about a software
+ component for asset tracking and/or installation dependency
+ management. When the IsEntity property has the value TRUE, the
+ instance of SoftwareIdentity represents an individually identifiable
+ entity similar to Physical Element. SoftwareIdentity does NOT indicate
+ whether the software is installed, executing, etc. This extra
+ information may be provided through specialized associations to
+ Software Identity. For instance, both InstalledSoftwareIdentity and
+ ElementSoftwareIdentity may be used to indicate that the software
+ identified by this class is installed. SoftwareIdentity is used when
+ managing the software components of a ManagedElement that is the
+ management focus. Since software may be acquired, SoftwareIdentity can
+ be associated with a Product using the ProductSoftwareComponent
+ relationship. The Application Model manages the deployment and
+ installation of software via the classes, SoftwareFeatures and
+ SoftwareElements. SoftwareFeature and SoftwareElement are used when
+ the software component is the management focus. The
+ deployment/installation concepts are related to the asset/identity
+ one. In fact, a SoftwareIdentity may correspond to a Product, or to
+ one or more SoftwareFeatures or SoftwareElements - depending on the
+ granularity of these classes and the deployment model. The
+ correspondence of Software Identity to Product, SoftwareFeature or
+ SoftwareElement is indicated using the ConcreteIdentity association.
+ Note that there may not be sufficient detail or instrumentation to
+ instantiate ConcreteIdentity. And, if the association is instantiated,
+ some duplication of information may result. For example, the Vendor
+ described in the instances of Product and SoftwareIdentity MAY be the
+ same. However, this is not necessarily true, and it is why vendor and
+ similar information are duplicated in this class. Note that
+ ConcreteIdentity can also be used to describe the relationship of the
+ software to any LogicalFiles that result from installing it. As above,
+ there may not be sufficient detail or instrumentation to instantiate
+ this association.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ with YumDB.get_instance():
+ pkg_info = Identity.object_path2pkg(model.path, 'all')
+ return Identity.pkg2model(pkg_info, keys_only=False, model=model)
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'InstanceID': None})
+
+ with YumDB.get_instance() as ydb:
+ pkglist = ydb.get_package_list(
+ 'all', allow_duplicates=True, sort=True)
+ for pkg_info in pkglist:
+ yield Identity.pkg2model(pkg_info, keys_only=keys_only, model=model)
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
diff --git a/src/software/openlmi/software/LMI_SoftwarePackage.py b/src/software/openlmi/software/LMI_SoftwareIdentityResource.py
index c2f9d3a..1377520 100644
--- a/src/software/openlmi/software/LMI_SoftwarePackage.py
+++ b/src/software/openlmi/software/LMI_SoftwareIdentityResource.py
@@ -1,4 +1,3 @@
-# -*- encoding: utf-8 -*-
# Software Management Providers
#
# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
@@ -20,9 +19,9 @@
# Authors: Michal Minar <miminar@redhat.com>
#
-"""Python Provider for LMI_SoftwarePackage
+"""Python Provider for LMI_SoftwareIdentityResource
-Instruments the CIM class LMI_SoftwarePackage
+Instruments the CIM class LMI_SoftwareIdentityResource
"""
@@ -30,20 +29,30 @@ import pywbem
from pywbem.cim_provider2 import CIMProvider2
from openlmi.common import cmpi_logging
-from openlmi.software.core import SoftwarePackage
-from openlmi.software.yumdb import YumDB
-
-class LMI_SoftwarePackage(CIMProvider2):
- """Instrument the CIM class LMI_SoftwarePackage
-
- RPM package installed on particular computer system with YUM (The
- Yellowdog Updated, Modified) package manager.
+from openlmi.software.core import IdentityResource
+from openlmi.software.yumdb import YumDB, errors
+
+class LMI_SoftwareIdentityResource(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentityResource
+
+ SoftwareIdentityResource describes the URL of a file or other resource
+ that contains all or part of of a SoftwareIdentity for use by the
+ SoftwareInstallationService. For example, a CIM_SoftwareIdentity might
+ consist of a meta data file, a binary executable file, and a
+ installability checker file for some software on a system. This class
+ allows a management client to selectively access the constituents of
+ the install package to perform a check function, or retrieve some meta
+ data information for the install package represented by the
+ SoftwareIdentity class without downloading the entire package.
+ SoftwareIdentityResources will be related to the SoftwareIdentity
+ using the SAPAvailableForElement association.
"""
- def __init__(self, _env):
+ def __init__ (self, _env):
cmpi_logging.logger.debug('Initializing provider %s from %s' \
% (self.__class__.__name__, __file__))
+ self.values = IdentityResource.Values
@cmpi_logging.trace_method
def get_instance(self, env, model):
@@ -66,12 +75,9 @@ class LMI_SoftwarePackage(CIMProvider2):
CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
Instance does not exist in the specified namespace)
CIM_ERR_FAILED (some other unspecified error occurred)
-
"""
- with YumDB.getInstance():
- pkg_info = SoftwarePackage.object_path2pkg(model.path, 'all')
- return SoftwarePackage.pkg2model(
- pkg_info, keys_only=False, model=model)
+ repo = IdentityResource.object_path2repo(env, model.path, kind='all')
+ return IdentityResource.repo2model(repo, keys_only=False, model=model)
@cmpi_logging.trace_method
def enum_instances(self, env, model, keys_only):
@@ -99,17 +105,13 @@ class LMI_SoftwarePackage(CIMProvider2):
# Prime model.path with knowledge of the keys, so key values on
# the CIMInstanceName (model.path) will automatically be set when
# we set property values on the model.
- model.path.update({'TargetOperatingSystem': None, 'Version': None,
- 'SoftwareElementState': None, 'Name': None,
- 'SoftwareElementID': None})
-
- with YumDB.getInstance() as ydb:
- # get all packages
- pkglist = ydb.get_package_list('all',
- allow_duplicates=True, sort=True)
- for pkg in pkglist:
- yield SoftwarePackage.pkg2model(pkg,
- keys_only=keys_only, model=model)
+ model.path.update({'CreationClassName': None, 'SystemName': None,
+ 'Name': None, 'SystemCreationClassName': None})
+
+ repolist = YumDB.get_instance().get_repository_list('all')
+ for repo in repolist:
+ yield IdentityResource.repo2model(
+ repo, keys_only=keys_only, model=model)
@cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
@@ -138,6 +140,7 @@ class LMI_SoftwarePackage(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
+ # TODO creation and modification should be supported
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
@cmpi_logging.trace_method
@@ -162,29 +165,65 @@ class LMI_SoftwarePackage(CIMProvider2):
CIM_ERR_FAILED (some other unspecified error occurred)
"""
+ # TODO removal should also be supported
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
@cmpi_logging.trace_method
- def cim_method_install(self, env, object_name):
- """Implements LMI_SoftwarePackage.Install()
-
- Will install available package.
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareIdentityResource.RequestStateChange()
+
+ Requests that the state of the element be changed to the value
+ specified in the RequestedState parameter. When the requested
+ state change takes place, the EnabledState and RequestedState of
+ the element will be the same. Invoking the RequestStateChange
+ method multiple times could result in earlier requests being
+ overwritten or lost. A return code of 0 shall indicate the state
+ change was successfully initiated. A return code of 3 shall
+ indicate that the state transition cannot complete within the
+ interval specified by the TimeoutPeriod parameter. A return code
+ of 4096 (0x1000) shall indicate the state change was successfully
+ initiated, a ConcreteJob has been created, and its reference
+ returned in the output parameter Job. Any other return code
+ indicates an error condition.
Keyword arguments:
env -- Provider Environment (pycimmb.ProviderEnvironment)
object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method Update()
+ specifying the object on which the method RequestStateChange()
should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16 Values.RequestStateChange.RequestedState)
+ The state requested for the element. This information will be
+ placed into the RequestedState property of the instance if the
+ return code of the RequestStateChange method is 0 ('Completed
+ with No Error'), or 4096 (0x1000) ('Job Started'). Refer to
+ the description of the EnabledState and RequestedState
+ properties for the detailed explanations of the RequestedState
+ values.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' shall be returned.
+
Returns a two-tuple containing the return value (
- type pywbem.Uint32 SoftwarePackage.Values.Install)
+ type pywbem.Uint32 Values.RequestStateChange)
and a list of CIMParameter objects representing the output parameters
Output parameters:
- Installed -- (type REF (pywbem.CIMInstanceName(
- classname='LMI_SoftwareInstalledPackage', ...))
- The reference to newly installed package, if installation was
- successful. Null otherwise.
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ May contain a reference to the ConcreteJob created to track the
+ state transition initiated by the method invocation.
Possible Errors:
CIM_ERR_ACCESS_DENIED
@@ -195,64 +234,37 @@ class LMI_SoftwarePackage(CIMProvider2):
CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
the invocation request)
CIM_ERR_FAILED (some other unspecified error occurred)
-
"""
- with YumDB.getInstance() as ydb:
- # get available packages
- pkg_info = SoftwarePackage.object_path2pkg_search(object_name)
- out_params = [ pywbem.CIMParameter('Installed', type='reference') ]
- if pkg_info.installed:
- out_params[0].value = SoftwarePackage.pkg2model(
- pkg_info, keys_only=True)
- return ( SoftwarePackage.Values.Install.Already_installed
- , out_params)
-
- cmpi_logging.logger.info('installing package %s' % pkg_info)
- installed_pkg = ydb.install_package(pkg_info)
- cmpi_logging.logger.info('package %s installed' % pkg_info)
-
- out_params[0].value = SoftwarePackage.pkg2model(
- installed_pkg, keys_only=True)
- return ( SoftwarePackage.Values.Install.Successful_installation
+ out_params = []
+ if param_timeoutperiod is not None:
+ return ( self.values.RequestStateChange. \
+ Use_of_Timeout_Parameter_Not_Supported
+ , out_params)
+ if param_requestedstate not in {
+ self.values.RequestStateChange.RequestedState.Enabled,
+ self.values.RequestStateChange.RequestedState.Disabled }:
+ return ( self.values.RequestStateChange.Invalid_State_Transition
, out_params)
- @cmpi_logging.trace_method
- def cim_method_remove(self, env, object_name):
- """Implements LMI_SoftwarePackage.Remove()
-
- Will uninstall installed package.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method Remove()
- should be invoked.
-
- Returns a two-tuple containing the return value (
- type pywbem.Uint32 SoftwarePackage.Values.Remove)
- and a list of CIMParameter objects representing the output parameters
-
- Output parameters: none
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
- unrecognized or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
- exist in the specified namespace)
- CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
- the invocation request)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- with YumDB.getInstance() as ydb:
- pkg_info = SoftwarePackage.object_path2pkg(object_name, 'all')
- if pkg_info.installed:
- cmpi_logging.logger.info('removing package %s' % pkg_info)
- ydb.remove_package(pkg_info)
- cmpi_logging.logger.info('package %s removed' % pkg_info)
- rval = SoftwarePackage.Values.Remove.Successful_removal
- else:
- rval = SoftwarePackage.Values.Remove.Not_installed
- return (rval, [])
+ with YumDB.get_instance() as ydb:
+ repo = IdentityResource.object_path2repo(env,
+ object_name, 'all')
+ enable = param_requestedstate == \
+ self.values.RequestStateChange.RequestedState.Enabled
+ cmpi_logging.logger.info("%s repository %s" % ("enabling"
+ if enable else "disabling", repo))
+ try:
+ prev = ydb.set_repository_enabled(
+ repo, enable)
+ except errors.RepositoryChangeError as exc:
+ msg = "failed to modify repository %s: %s" % (repo, str(exc))
+ cmpi_logging.logger.error(msg)
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, msg)
+ msg = ( "repository %s already %s"
+ if prev == enable else "repository %s %s")
+ cmpi_logging.logger.info(msg % (repo,
+ "enabled" if enable else "disabled"))
+
+ rval = self.values.RequestStateChange.Completed_with_No_Error
+ return (rval, out_params)
diff --git a/src/software/openlmi/software/LMI_SoftwareInstallationJob.py b/src/software/openlmi/software/LMI_SoftwareInstallationJob.py
new file mode 100644
index 0000000..ba0867f
--- /dev/null
+++ b/src/software/openlmi/software/LMI_SoftwareInstallationJob.py
@@ -0,0 +1,380 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareInstallationJob
+
+Instruments the CIM class LMI_SoftwareInstallationJob
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import InstallationJob
+from openlmi.software.yumdb import errors, YumDB
+
+class LMI_SoftwareInstallationJob(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstallationJob
+
+ A concrete version of Job. This class represents a generic and
+ instantiable unit of work, such as a batch or a print job.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = InstallationJob.Values
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ job = InstallationJob.object_path2job(model.path)
+ return InstallationJob.job2model(
+ job, keys_only=False, model=model)
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'InstanceID': None})
+
+ for job in YumDB.get_instance().get_job_list():
+ yield InstallationJob.job2model(job, keys_only, model)
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ if not modify_existing:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
+ "Can not create new instance.")
+ return InstallationJob.modify_instance(instance)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ with YumDB.get_instance() as ydb:
+ job = InstallationJob.object_path2job(instance_name)
+ try:
+ cmpi_logging.logger.info('deleting job "%s"' % job)
+ ydb.delete_job(job.jobid)
+ cmpi_logging.logger.info('job "%s" removed' % job)
+ except errors.JobNotFound as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ getattr(exc, 'message', str(exc)))
+ except errors.JobControlError as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ getattr(exc, 'message', str(exc)))
+
+ @cmpi_logging.trace_method
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareInstallationJob.RequestStateChange()
+
+ Requests that the state of the job be changed to the value
+ specified in the RequestedState parameter. Invoking the
+ RequestStateChange method multiple times could result in earlier
+ requests being overwritten or lost. If 0 is returned, then the
+ task completed successfully. Any other return code indicates an
+ error condition.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method RequestStateChange()
+ should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16
+ self.Values.RequestStateChange.RequestedState)
+ RequestStateChange changes the state of a job. The possible
+ values are as follows: Start (2) changes the state to
+ 'Running'. Suspend (3) stops the job temporarily. The
+ intention is to subsequently restart the job with 'Start'. It
+ might be possible to enter the 'Service' state while
+ suspended. (This is job-specific.) Terminate (4) stops the
+ job cleanly, saving data, preserving the state, and shutting
+ down all underlying processes in an orderly manner. Kill (5)
+ terminates the job immediately with no requirement to save
+ data or preserve the state. Service (6) puts the job into a
+ vendor-specific service state. It might be possible to restart
+ the job.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' must be returned.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.RequestStateChange)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ job = InstallationJob.object_path2job(object_name)
+ try:
+ if param_requestedstate not in {
+ self.values.RequestStateChange.RequestedState.Terminate }:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Valid RequestedState can by only Terminate (%d)" %
+ self.values.RequestStateChange.RequestedState.Terminate)
+ if param_timeoutperiod:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Timeout period is not supported.")
+ YumDB.get_instance().terminate_job(job.jobid)
+ except errors.JobNotFound as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ getattr(exc, 'message', str(exc)))
+ except errors.JobControlError as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ getattr(exc, 'message', str(exc)))
+ return (self.values.GetErrors.Success, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_geterrors(self, env, object_name):
+ """Implements LMI_SoftwareInstallationJob.GetErrors()
+
+ If JobState is "Completed" and Operational Status is "Completed"
+ then no instance of CIM_Error is returned. If JobState is
+ "Exception" then GetErrors may return intances of CIM_Error
+ related to the execution of the procedure or method invoked by the
+ job. If Operatational Status is not "OK" or "Completed"then
+ GetErrors may return CIM_Error instances related to the running of
+ the job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method GetErrors()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.GetErrors)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Errors -- (type pywbem.CIMInstance(classname='CIM_Error', ...))
+ If the OperationalStatus on the Job is not "OK", then this
+ method will return one or more CIM Error instance(s).
+ Otherwise, when the Job is "OK", null is returned.
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ job = InstallationJob.object_path2job(object_name)
+ error = InstallationJob.job2error(job)
+ out_params = []
+ if error is not None:
+ param = pywbem.CIMParameter('Errors', type='instance',
+ is_array=True, array_size=1, value=[error])
+ out_params.append(param)
+ return (self.values.GetErrors.Success, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_killjob(self, env, object_name,
+ param_deleteonkill=None):
+ """Implements LMI_SoftwareInstallationJob.KillJob()
+
+ KillJob is being deprecated because there is no distinction made
+ between an orderly shutdown and an immediate kill.
+ CIM_ConcreteJob.RequestStateChange() provides 'Terminate' and
+ 'Kill' options to allow this distinction. A method to kill this
+ job and any underlying processes, and to remove any 'dangling'
+ associations.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method KillJob()
+ should be invoked.
+ param_deleteonkill -- The input parameter DeleteOnKill (type bool)
+ Indicates whether or not the Job should be automatically
+ deleted upon termination. This parameter takes precedence over
+ the property, DeleteOnCompletion.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.KillJob)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_geterror(self, env, object_name):
+ """Implements LMI_SoftwareInstallationJob.GetError()
+
+ GetError is deprecated because Error should be an array,not a
+ scalar. When the job is executing or has terminated without error,
+ then this method returns no CIM_Error instance. However, if the
+ job has failed because of some internal problem or because the job
+ has been terminated by a client, then a CIM_Error instance is
+ returned.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method GetError()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.GetError)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Error -- (type pywbem.CIMInstance(classname='CIM_Error', ...))
+ If the OperationalStatus on the Job is not "OK", then this
+ method will return a CIM Error instance. Otherwise, when the
+ Job is "OK", null is returned.
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ job = InstallationJob.object_path2job(object_name)
+ error = InstallationJob.job2error(job)
+ out_params = []
+ if error is not None:
+ param = pywbem.CIMParameter('Errors', type='instance', value=error)
+ out_params.append(param)
+ return (self.values.GetErrors.Success, out_params)
diff --git a/src/software/openlmi/software/LMI_SoftwareInstallationService.py b/src/software/openlmi/software/LMI_SoftwareInstallationService.py
new file mode 100644
index 0000000..8d29b3d
--- /dev/null
+++ b/src/software/openlmi/software/LMI_SoftwareInstallationService.py
@@ -0,0 +1,783 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareInstallationService
+
+Instruments the CIM class LMI_SoftwareInstallationService
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import InstallationJob
+from openlmi.software.core import InstallationService
+
+class LMI_SoftwareInstallationService(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstallationService
+
+ A subclass of service which provides methods to install (or update)
+ Software Identities in ManagedElements.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = InstallationService.Values
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ InstallationService.check_path(env, model.path, "path")
+
+ objpath = InstallationService.get_path()
+
+ for key, value in objpath.keybindings.items():
+ model[key] = value
+
+ model['Caption'] = 'Software installation service for this system.'
+ model['CommunicationStatus'] = self.values. \
+ CommunicationStatus.Not_Available
+ model['Description'] = \
+ 'Software installation service using YUM packake manager.'
+ model['DetailedStatus'] = self.values.DetailedStatus.Not_Available
+ model['EnabledDefault'] = self.values.EnabledDefault.Not_Applicable
+ model['EnabledState'] = self.values.EnabledState.Not_Applicable
+ model['HealthState'] = self.values.HealthState.OK
+ model['InstanceID'] = 'LMI:InstallationService'
+ model['OperatingStatus'] = self.values.OperatingStatus.Servicing
+ model['OperationalStatus'] = [self.values.OperationalStatus.OK]
+ model['PrimaryStatus'] = self.values.PrimaryStatus.OK
+ model['RequestedState'] = self.values.RequestedState.Not_Applicable
+ model['Started'] = True
+ model['TransitioningToState'] = self.values. \
+ TransitioningToState.Not_Applicable
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ model.path.update({'CreationClassName': None, 'SystemName': None,
+ 'Name': None, 'SystemCreationClassName': None})
+
+ objpath = InstallationService.get_path()
+ for key, value in objpath.keybindings.items():
+ model[key] = value
+ if not keys_only:
+ model = self.get_instance(env, model)
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareInstallationService.RequestStateChange()
+
+ Requests that the state of the element be changed to the value
+ specified in the RequestedState parameter. When the requested
+ state change takes place, the EnabledState and RequestedState of
+ the element will be the same. Invoking the RequestStateChange
+ method multiple times could result in earlier requests being
+ overwritten or lost. A return code of 0 shall indicate the state
+ change was successfully initiated. A return code of 3 shall
+ indicate that the state transition cannot complete within the
+ interval specified by the TimeoutPeriod parameter. A return code
+ of 4096 (0x1000) shall indicate the state change was successfully
+ initiated, a ConcreteJob has been created, and its reference
+ returned in the output parameter Job. Any other return code
+ indicates an error condition.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method RequestStateChange()
+ should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16 self.Values.RequestStateChange.RequestedState)
+ The state requested for the element. This information will be
+ placed into the RequestedState property of the instance if the
+ return code of the RequestStateChange method is 0 ('Completed
+ with No Error'), or 4096 (0x1000) ('Job Started'). Refer to
+ the description of the EnabledState and RequestedState
+ properties for the detailed explanations of the RequestedState
+ values.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' shall be returned.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.RequestStateChange)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ May contain a reference to the ConcreteJob created to track the
+ state transition initiated by the method invocation.
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ out_params = []
+ out_params += [pywbem.CIMParameter('Job', type='reference', value=None)]
+ return ( self.values.RequestStateChange.Not_Supported
+ , out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_stopservice(self, env, object_name):
+ """Implements LMI_SoftwareInstallationService.StopService()
+
+ The StopService method places the Service in the stopped state.
+ Note that the function of this method overlaps with the
+ RequestedState property. RequestedState was added to the model to
+ maintain a record (such as a persisted value) of the last state
+ request. Invoking the StopService method should set the
+ RequestedState property appropriately. The method returns an
+ integer value of 0 if the Service was successfully stopped, 1 if
+ the request is not supported, and any other number to indicate an
+ error. In a subclass, the set of possible return codes could be
+ specified using a ValueMap qualifier on the method. The strings to
+ which the ValueMap contents are translated can also be specified
+ in the subclass as a Values array qualifier. Note: The semantics
+ of this method overlap with the RequestStateChange method that is
+ inherited from EnabledLogicalElement. This method is maintained
+ because it has been widely implemented, and its simple "stop"
+ semantics are convenient to use.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method StopService()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ rval = pywbem.Uint32(1) # Not Supported
+ return (rval, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_installfromuri(self, env, object_name,
+ param_installoptionsvalues=None,
+ param_uri=None,
+ param_installoptions=None,
+ param_target=None):
+ """Implements LMI_SoftwareInstallationService.InstallFromURI()
+
+ Start a job to install software from a specific URI in a
+ ManagedElement. Note that this method is provided to support
+ existing, alternative download mechanisms (such as used for
+ firmware download). The 'normal' mechanism will be to use the
+ InstallFromSoftwareIdentity method. If 0 is returned, the function
+ completed successfully and no ConcreteJob instance was required.
+ If 4096/0x1000 is returned, a ConcreteJob will be started to to
+ perform the install. The Job's reference will be returned in the
+ output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InstallFromURI()
+ should be invoked.
+ param_installoptionsvalues -- The input parameter \
+ InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additionalinformation to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. For
+ further information on the use of InstallOptionsValues
+ parameter, see the description of the InstallOptionsValues
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity
+ method.
+
+ param_uri -- The input parameter URI (type unicode)
+ A URI for the software based on RFC 2079.
+
+ param_installoptions -- The input parameter InstallOptions (
+ type [pywbem.Uint16,] self.Values.InstallFromURI.InstallOptions)
+ Options to control the install process. See the InstallOptions
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity method
+ for the description of these values.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.InstallFromURI)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ try:
+ jobid = InstallationService.install_or_remove_package(
+ env, "uri", param_uri,
+ param_target, None, param_installoptions,
+ param_installoptionsvalues)
+ rval = self.values.InstallFromURI.Job_Completed_with_No_Error
+ out_params[0].value = InstallationJob.job2model(jobid)
+ except InstallationService.InstallationError as exc:
+ cmpi_logging.logger.error(
+ "installation failed: %s", exc.description)
+ rval = exc.return_code
+ return (rval, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_checksoftwareidentity(self, env, object_name,
+ param_source=None,
+ param_target=None,
+ param_collection=None):
+ """Implements LMI_SoftwareInstallationService.CheckSoftwareIdentity()
+
+ This method allows a client application to determine whether a
+ specific SoftwareIdentity can be installed (or updated) on a
+ ManagedElement. It also allows other characteristics to be
+ determined such as whether install will require a reboot. In
+ addition a client can check whether the SoftwareIdentity can be
+ added simulataneously to a specified SofwareIndentityCollection. A
+ client MAY specify either or both of the Collection and Target
+ parameters. The Collection parameter is only supported if
+ SoftwareInstallationServiceCapabilities.CanAddToCollection is
+ TRUE.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method CheckSoftwareIdentity()
+ should be invoked.
+ param_source -- The input parameter Source (type REF (
+ pywbem.CIMInstanceName(classname='CIM_SoftwareIdentity', ...))
+ Reference to the SoftwareIdentity to be checked.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ Reference to the ManagedElement that the Software Identity is
+ going to be installed in (or updated).
+
+ param_collection -- The input parameter Collection (type REF (
+ pywbem.CIMInstanceName(classname='CIM_Collection', ...))
+ Reference to the Collection to which the Software Identity will
+ be added.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.CheckSoftwareIdentity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ InstallCharacteristics -- (type [pywbem.Uint16,]
+ self.Values.CheckSoftwareIdentity.InstallCharacteristics)
+ The parameter describes the characteristics of the
+ installation/update that will take place if the Source
+ Software Identity is installed: Target automatic reset: The
+ target element will automatically reset once the installation
+ is complete. System automatic reset: The containing system of
+ the target ManagedElement (normally a logical device or the
+ system itself) will automatically reset/reboot once the
+ installation is complete. Separate target reset required:
+ EnabledLogicalElement.RequestStateChange MUST be used to reset
+ the target element after the SoftwareIdentity is installed.
+ Separate system reset required:
+ EnabledLogicalElement.RequestStateChange MUST be used to
+ reset/reboot the containing system of the target
+ ManagedElement after the SoftwareIdentity is installed.
+ Manual Reboot Required: The system MUST be manually rebooted
+ by the user. No reboot required : No reboot is required after
+ installation. User Intervention Recomended : It is
+ recommended that a user confirm installation of this
+ SoftwareIdentity. Inappropriate application MAY have serious
+ consequences. MAY be added to specified collection : The
+ SoftwareIndentity MAY be added to specified Collection.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_changeaffectedelementsassignedsequence(self,
+ env, object_name,
+ param_managedelements,
+ param_assignedsequence):
+ """Implements LMI_SoftwareInstallationService. \
+ ChangeAffectedElementsAssignedSequence()
+
+ This method is called to change relative sequence in which order
+ the ManagedElements associated to the Service through
+ CIM_ServiceAffectsElement association are affected. In the case
+ when the Service represents an interface for client to execute
+ extrinsic methods and when it is used for grouping of the managed
+ elements that could be affected, the ordering represents the
+ relevant priority of the affected managed elements with respect to
+ each other. An ordered array of ManagedElement instances is
+ passed to this method, where each ManagedElement instance shall be
+ already be associated with this Service instance via
+ CIM_ServiceAffectsElement association. If one of the
+ ManagedElements is not associated to the Service through
+ CIM_ServiceAffectsElement association, the implementation shall
+ return a value of 2 ("Error Occured"). Upon successful execution
+ of this method, if the AssignedSequence parameter is NULL, the
+ value of the AssignedSequence property on each instance of
+ CIM_ServiceAffectsElement shall be updated such that the values of
+ AssignedSequence properties shall be monotonically increasing in
+ correlation with the position of the referenced ManagedElement
+ instance in the ManagedElements input parameter. That is, the
+ first position in the array shall have the lowest value for
+ AssignedSequence. The second position shall have the second lowest
+ value, and so on. Upon successful execution, if the
+ AssignedSequence parameter is not NULL, the value of the
+ AssignedSequence property of each instance of
+ CIM_ServiceAffectsElement referencing the ManagedElement instance
+ in the ManagedElements array shall be assigned the value of the
+ corresponding index of the AssignedSequence parameter array. For
+ ManagedElements instances which are associated with the Service
+ instance via CIM_ServiceAffectsElement and are not present in the
+ ManagedElements parameter array, the AssignedSequence property on
+ the CIM_ServiceAffects association shall be assigned a value of 0.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method
+ ChangeAffectedElementsAssignedSequence() should be invoked.
+ param_managedelements -- The input parameter ManagedElements (
+ type REF (pywbem.CIMInstanceName(
+ classname='CIM_ManagedElement', ...)) (Required)
+ An array of ManagedElements.
+
+ param_assignedsequence -- The input parameter AssignedSequence (
+ type [pywbem.Uint16,]) (Required)
+ An array of integers representing AssignedSequence for the
+ ManagedElement in the corresponding index of the
+ ManagedElements parameter.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values. \
+ ChangeAffectedElementsAssignedSequence)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job spawned if the operation continues after
+ the method returns. (May be null if the task is completed).
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_installfromsoftwareidentity(
+ self, env, object_name,
+ param_installoptions=None,
+ param_target=None,
+ param_collection=None,
+ param_source=None,
+ param_installoptionsvalues=None):
+ """Implements LMI_SoftwareInstallationService. \
+ InstallFromSoftwareIdentity()
+
+ Start a job to install or update a SoftwareIdentity (Source) on a
+ ManagedElement (Target). In addition the method can be used to
+ add the SoftwareIdentity simulataneously to a specified
+ SofwareIndentityCollection. A client MAY specify either or both of
+ the Collection and Target parameters. The Collection parameter is
+ only supported if InstallationService.CanAddToCollection
+ is TRUE. If 0 is returned, the function completed successfully
+ and no ConcreteJob instance was required. If 4096/0x1000 is
+ returned, a ConcreteJob will be started to perform the install.
+ The Job's reference will be returned in the output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method
+ InstallFromSoftwareIdentity()
+ should be invoked.
+ param_installoptions -- The input parameter InstallOptions (
+ type [pywbem.Uint16,] self.Values.InstallFromSoftwareIdentity.\
+ InstallOptions)
+ Options to control the install process. Defer target/system
+ reset : do not automatically reset the target/system. Force
+ installation : Force the installation of the same or an older
+ SoftwareIdentity. Install: Perform an installation of this
+ software on the managed element. Update: Perform an update of
+ this software on the managed element. Repair: Perform a repair
+ of the installation of this software on the managed element by
+ forcing all the files required for installing the software to
+ be reinstalled. Reboot: Reboot or reset the system immediately
+ after the install or update of this software, if the install
+ or the update requires a reboot or reset. Password: Password
+ will be specified as clear text without any encryption for
+ performing the install or update. Uninstall: Uninstall the
+ software on the managed element. Log: Create a log for the
+ install or update of the software. SilentMode: Perform the
+ install or update without displaying any user interface.
+ AdministrativeMode: Perform the install or update of the
+ software in the administrative mode. ScheduleInstallAt:
+ Indicates the time at which theinstall or update of the
+ software will occur.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target. If NULL then the SOftwareIdentity will
+ be added to Collection only. The underlying implementation is
+ expected to be able to obtain any necessary metadata from the
+ Software Identity.
+
+ param_collection -- The input parameter Collection (type REF (
+ pywbem.CIMInstanceName(classname='CIM_Collection', ...))
+ Reference to the Collection to which the Software Identity
+ SHALL be added. If NULL then the SOftware Identity will not be
+ added to a Collection.
+
+ param_source -- The input parameter Source (type REF (
+ pywbem.CIMInstanceName(classname='CIM_SoftwareIdentity', ...))
+ Reference to the source of the install.
+
+ param_installoptionsvalues -- The input parameter
+ InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additional information to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. If the
+ index in InstallOptions has the value "Password " then a value
+ at the corresponding index of InstallOptionValues shall not be
+ NULL. If the index in InstallOptions has the value
+ "ScheduleInstallAt" then the value at the corresponding index
+ of InstallOptionValues shall not be NULL and shall be in the
+ datetime type format. If the index in InstallOptions has the
+ value "Log " then a value at the corresponding index of
+ InstallOptionValues may be NULL. If the index in
+ InstallOptions has the value "Defer target/system reset",
+ "Force installation","Install", "Update", "Repair" or "Reboot"
+ then a value at the corresponding index of InstallOptionValues
+ shall be NULL.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.InstallFromSoftwareIdentity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ try:
+ jobid = InstallationService.install_or_remove_package(
+ env, "identity", param_source,
+ param_target, param_collection, param_installoptions,
+ param_installoptionsvalues)
+ rval = self.values.InstallFromSoftwareIdentity. \
+ Job_Completed_with_No_Error
+ out_params[0].value = InstallationJob.job2model(jobid)
+ except InstallationService.InstallationError as exc:
+ cmpi_logging.logger.error(
+ "installation failed: %s", exc.description)
+ rval = exc.return_code
+ return (rval, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_startservice(self, env, object_name):
+ """Implements LMI_SoftwareInstallationService.StartService()
+
+ The StartService method places the Service in the started state.
+ Note that the function of this method overlaps with the
+ RequestedState property. RequestedState was added to the model to
+ maintain a record (such as a persisted value) of the last state
+ request. Invoking the StartService method should set the
+ RequestedState property appropriately. The method returns an
+ integer value of 0 if the Service was successfully started, 1 if
+ the request is not supported, and any other number to indicate an
+ error. In a subclass, the set of possible return codes could be
+ specified using a ValueMap qualifier on the method. The strings to
+ which the ValueMap contents are translated can also be specified
+ in the subclass as a Values array qualifier. Note: The semantics
+ of this method overlap with the RequestStateChange method that is
+ inherited from EnabledLogicalElement. This method is maintained
+ because it has been widely implemented, and its simple "start"
+ semantics are convenient to use.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method StartService()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+ """
+ rval = pywbem.Uint32(1) # Not Supported
+ return (rval, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_installfrombytestream(self, env, object_name,
+ param_installoptionsvalues=None,
+ param_image=None,
+ param_installoptions=None,
+ param_target=None):
+ """Implements LMI_SoftwareInstallationService.InstallFromByteStream()
+
+ Start a job to download a series of bytes containing a software
+ image to a ManagedElement. Note that this method is provided to
+ support existing, alternative download mechanisms (such as used
+ for firmware download). The 'normal' mechanism will be to use the
+ InstallFromSoftwareIdentity method. If 0 is returned, the
+ function completed successfully and no ConcreteJob instance was
+ required. If 4096/0x1000 is returned, a ConcreteJob will be
+ started to to perform the install. The Job's reference will be
+ returned in the output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InstallFromByteStream()
+ should be invoked.
+ param_installoptionsvalues -- The input parameter InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additional information to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. For
+ further information on the use of InstallOptionsValues
+ parameter, see the description of the InstallOptionsValues
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity
+ method.
+
+ param_image -- The input parameter Image (type [pywbem.Uint8,])
+ A array of bytes containing the install image.
+
+ param_installoptions -- The input parameter InstallOptions (type [pywbem.Uint16,] self.Values.InstallFromByteStream.InstallOptions)
+ Options to control the install process. See the InstallOptions
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity method
+ for the description of these values.
+
+ param_target -- The input parameter Target (type REF (pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target.
+
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.InstallFromByteStream)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ return ( self.values.InstallFromByteStream.Not_Supported
+ , out_params)
diff --git a/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py b/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py
deleted file mode 100644
index 22d214b..0000000
--- a/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py
+++ /dev/null
@@ -1,451 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""Python Provider for LMI_SoftwareInstalledPackage
-
-Instruments the CIM class LMI_SoftwareInstalledPackage
-
-"""
-
-import pywbem
-from pywbem.cim_provider2 import CIMProvider2
-
-from openlmi.common import cmpi_logging
-from openlmi.software.core import (
- ComputerSystem, SoftwareFileCheck,
- SoftwareInstalledPackage, SoftwarePackage)
-from openlmi.software.yumdb import YumDB
-
-class LMI_SoftwareInstalledPackage(CIMProvider2):
- """Instrument the CIM class LMI_SoftwareInstalledPackage
-
- The InstalledSoftwareElement association allows the identification of
- the ComputerSystem on which a particular SoftwareElement is installed.
-
- """
-
- def __init__ (self, _env):
- cmpi_logging.logger.debug('Initializing provider %s from %s' \
- % (self.__class__.__name__, __file__))
-
- @cmpi_logging.trace_method
- def get_instance(self, env, model):
- """Return an instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- model -- A template of the pywbem.CIMInstance to be returned. The
- key properties are set on this instance to correspond to the
- instanceName that was requested. The properties of the model
- are already filtered according to the PropertyList from the
- request. Only properties present in the model need to be
- given values. If you prefer, you can set all of the
- values, and the instance will be filtered for you.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
- Instance does not exist in the specified namespace)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- if not "Software" in model:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Software property.")
- if not "System" in model:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing System property.")
- ComputerSystem.check_path_property(env, model, 'System')
- model['System'] = ComputerSystem.get_path()
- with YumDB.getInstance():
- pkg_info = SoftwarePackage.object_path2pkg(model['Software'],
- kind="installed")
- model['Software'] = SoftwarePackage.pkg2model(
- pkg_info, keys_only=True)
- return model
-
- @cmpi_logging.trace_method
- def enum_instances(self, env, model, keys_only):
- """Enumerate instances.
-
- The WBEM operations EnumerateInstances and EnumerateInstanceNames
- are both mapped to this method.
- This method is a python generator
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- model -- A template of the pywbem.CIMInstances to be generated.
- The properties of the model are already filtered according to
- the PropertyList from the request. Only properties present in
- the model need to be given values. If you prefer, you can
- always set all of the values, and the instance will be filtered
- for you.
- keys_only -- A boolean. True if only the key properties should be
- set on the generated instances.
-
- Possible Errors:
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- # Prime model.path with knowledge of the keys, so key values on
- # the CIMInstanceName (model.path) will automatically be set when
- # we set property values on the model.
- model.path.update({'System': None, 'Software': None})
-
- yum_package_path = pywbem.CIMInstanceName("LMI_SoftwarePackage",
- namespace=model.path.namespace,
- host=model.path.host)
- model['System'] = ComputerSystem.get_path()
- with YumDB.getInstance() as ydb:
- pkglist = ydb.get_package_list('installed')
- for pkg in pkglist:
- iname = SoftwarePackage.pkg2model(pkg, model=yum_package_path)
- model['Software'] = iname
- yield model
-
- @cmpi_logging.trace_method
- def set_instance(self, env, instance, modify_existing):
- """Return a newly created or modified instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- instance -- The new pywbem.CIMInstance. If modifying an existing
- instance, the properties on this instance have been filtered by
- the PropertyList from the request.
- modify_existing -- True if ModifyInstance, False if CreateInstance
-
- Return the new instance. The keys must be set on the new instance.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_NOT_SUPPORTED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
- valid if modify_existing is False, indicating that the operation
- was CreateInstance)
- CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
- if modify_existing is True, indicating that the operation
- was ModifyInstance)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- # parse and check arguments
- if modify_existing is True:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
- "MofifyInstance is not supported")
-
- if not "Software" in instance:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Software property.")
- if not "System" in instance:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing System property.")
- ComputerSystem.check_path_property(env, instance, 'System')
-
- with YumDB.getInstance() as ydb:
- pkg_info = SoftwarePackage.object_path2pkg_search(
- instance['Software'])
-
- if pkg_info.installed:
- raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
- "Package %s is already installed." % pkg_info)
-
- cmpi_logging.logger.info('installing package %s' % pkg_info)
- installed_pkg = ydb.install_package(pkg_info)
- cmpi_logging.logger.info('package %s installed' % pkg_info)
-
- instance["Software"] = SoftwarePackage.pkg2model(installed_pkg)
- return instance
-
- @cmpi_logging.trace_method
- def delete_instance(self, env, instance_name):
- """Delete an instance.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- instance_name -- A pywbem.CIMInstanceName specifying the instance
- to delete.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_NOT_SUPPORTED
- CIM_ERR_INVALID_NAMESPACE
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
- namespace)
- CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
- Instance does not exist in the specified namespace)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- if not "Software" in instance_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Software property.")
- if not "System" in instance_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing System property.")
- ComputerSystem.check_path_property(env, instance_name, 'System')
-
- with YumDB.getInstance() as ydb:
- pkg_info = SoftwarePackage.object_path2pkg(
- instance_name["Software"], kind="installed")
- cmpi_logging.logger.info('removing package "%s"' % pkg_info)
- ydb.remove_package(pkg_info)
- cmpi_logging.logger.info('package "%s" removed' % pkg_info)
-
- @cmpi_logging.trace_method
- def references(self, env, object_name, model, result_class_name, role,
- result_role, keys_only):
- """Instrument Associations.
-
- All four association-related operations (Associators, AssociatorNames,
- References, ReferenceNames) are mapped to this method.
- This method is a python generator
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName that defines the source
- CIM Object whose associated Objects are to be returned.
- model -- A template pywbem.CIMInstance to serve as a model
- of the objects to be returned. Only properties present on this
- model need to be set.
- result_class_name -- If not empty, this string acts as a filter on
- the returned set of Instances by mandating that each returned
- Instances MUST represent an association between object_name
- and an Instance of a Class whose name matches this parameter
- or a subclass.
- role -- If not empty, MUST be a valid Property name. It acts as a
- filter on the returned set of Instances by mandating that each
- returned Instance MUST refer to object_name via a Property
- whose name matches the value of this parameter.
- result_role -- If not empty, MUST be a valid Property name. It acts
- as a filter on the returned set of Instances by mandating that
- each returned Instance MUST represent associations of
- object_name to other Instances, where the other Instances play
- the specified result_role in the association (i.e. the
- name of the Property in the Association Class that refers to
- the Object related to object_name MUST match the value of this
- parameter).
- keys_only -- A boolean. True if only the key properties should be
- set on the generated instances.
-
- The following diagram may be helpful in understanding the role,
- result_role, and result_class_name parameters.
- +------------------------+ +-------------------+
- | object_name.classname | | result_class_name |
- | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
- +------------------------+ +-------------------+
- | +-----------------------------------+ |
- | | [Association] model.classname | |
- | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
- +--------------+ object_name.classname REF role | |
- (CIMInstanceName) | result_class_name REF result_role +------+
- | |(CIMInstanceName)
- +-----------------------------------+
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_NOT_SUPPORTED
- CIM_ERR_INVALID_NAMESPACE
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
- or otherwise incorrect parameters)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- cimhandle = env.get_cimom_handle()
-
- # If you want to get references for free, implemented in terms
- # of enum_instances, just leave the code below unaltered.
- if cimhandle.is_subclass(object_name.namespace,
- sub=object_name.classname,
- super='CIM_ComputerSystem') or \
- cimhandle.is_subclass(object_name.namespace,
- sub=object_name.classname,
- super='LMI_SoftwarePackage'):
- return self.simple_refs(env, object_name, model,
- result_class_name, role, result_role, keys_only)
-
- @cmpi_logging.trace_method
- def cim_method_checkintegrity(self, env, object_name):
- """Implements LMI_SoftwarePackage.CheckIntegrity()
-
- Verify existence and attributes of files installed from RPM
- package. If all files installed exist and their attributes
- matches, method returns "Pass". "Not passed" is returned when
- arbitrary file differs in its attributes. And "Error" if
- verification could not be done.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method CheckIntegrity()
- should be invoked.
-
- Returns a two-tuple containing the return value (
- type pywbem.Uint32 SoftwareInstalledPackage.Values.CheckIntegrity)
- and a list of CIMParameter objects representing the output parameters
-
- Output parameters:
- Failed -- (type REF (pywbem.CIMInstanceName(
- classname='LMI_SoftwareFileCheck', ...))
- Array of references to LMI_SoftwareFileCheck, that did not pass
- verification.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
- unrecognized or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
- exist in the specified namespace)
- CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
- the invocation request)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- if not "Software" in object_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Software property.")
- if not "System" in object_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing System property.")
-
- failed = []
- with YumDB.getInstance() as ydb:
- pkg_info = SoftwarePackage.object_path2pkg(object_name['Software'],
- kind="installed")
- pkg_check = ydb.check_package(pkg_info)
- for pkg_file in pkg_check.files.values():
- file_check = SoftwareFileCheck.test_file(
- pkg_check.file_checksum_type, pkg_file)
- if SoftwareFileCheck.filecheck_passed(file_check):
- continue
- failed.append(SoftwareFileCheck.filecheck2model(
- pkg_info, pkg_check, pkg_file.path,
- keys_only=True, file_check=file_check))
- out_params = [ pywbem.CIMParameter('Failed', type='reference',
- value=failed) ]
- return ( getattr(SoftwareInstalledPackage.Values.CheckIntegrity,
- 'Pass' if len(failed) == 0 else 'Not_passed')
- , out_params )
-
- @cmpi_logging.trace_method
- def cim_method_update(self, env, object_name,
- param_epoch=None,
- param_release=None,
- param_version=None):
- """Implements LMI_SoftwareInstalledPackage.Update()
-
- Updates the package to latest version. When any of "version" or
- "release" argument is given, install only matching available
- package. Otherwise try to update to newest package available.
-
- Keyword arguments:
- env -- Provider Environment (pycimmb.ProviderEnvironment)
- object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
- specifying the object on which the method Update()
- should be invoked.
- param_release -- The input parameter release (type unicode)
- Specify particular release of package to update to. Update to
- newest, when empty
- param_version -- The input parameter version (type unicode)
- Specify particular version of package to update to. Update to
- newest, when empty
-
- Returns a two-tuple containing the return value (
- type pywbem.Uint16 SoftwareInstalledPackage.Values.Update)
- and a list of CIMParameter objects representing the output parameters
-
- Output parameters:
- Installed -- (type REF (pywbem.CIMInstanceName(
- classname='LMI_SoftwareInstalledPackage', ...))
- The reference to newly installed package, if installation was
- successful. Otherwise reference to current package.
-
- Possible Errors:
- CIM_ERR_ACCESS_DENIED
- CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
- unrecognized or otherwise incorrect parameters)
- CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
- exist in the specified namespace)
- CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
- the invocation request)
- CIM_ERR_FAILED (some other unspecified error occurred)
-
- """
- if not "Software" in object_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing Software property.")
- if not "System" in object_name:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Missing System property.")
- ComputerSystem.check_path_property(env, object_name, 'System')
-
- with YumDB.getInstance() as ydb:
- orig = SoftwarePackage.object_path2pkg(object_name['Software'],
- kind='installed')
-
- # NOTE: that we need to obtain all available and installed packages
- # because they are disjunctive and we want to select package
- # with highest version. Let the yum do the sorting of packages.
- pkglist = ydb.filter_packages('all',
- allow_duplicates=True, sort=True,
- name=orig.name,
- epoch=param_epoch,
- version=param_version,
- release=param_release,
- arch=orig.arch)
-
- if len(pkglist) < 1:
- cmpi_logging.logger.error(
- "desired package matching evr not found")
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "package matching desired evr not found among"
- " available packages")
- if len(pkglist) > 1:
- cmpi_logging.logger.info("multiple packages matching"
- " evr - selecting newest")
- cmpi_logging.logger.debug("matching packages (from oldest):"
- " [%s]" % ", ".join([str(p) for p in pkglist]))
- desired_pkg = pkglist[-1]
-
- out_params = [pywbem.CIMParameter('Installed', type='reference')]
- if desired_pkg.installed:
- out_params[0].value = SoftwarePackage.pkg2model(desired_pkg)
- cmpi_logging.logger.info('already up to date')
- return ( SoftwareInstalledPackage.Values.Update.Already_newest
- , out_params)
-
- cmpi_logging.logger.info(
- 'trying to update package \"%s\" to "%s"' % (
- orig, desired_pkg))
- installed_pkg = ydb.update_to_package(desired_pkg)
- cmpi_logging.logger.info('update successful')
-
- out_params[0].value = SoftwarePackage.pkg2model(installed_pkg)
-
- return ( SoftwareInstalledPackage.Values.Update.Successful_installation
- , out_params)
-
diff --git a/src/software/openlmi/software/LMI_SoftwareMethodResult.py b/src/software/openlmi/software/LMI_SoftwareMethodResult.py
new file mode 100644
index 0000000..71c3ca9
--- /dev/null
+++ b/src/software/openlmi/software/LMI_SoftwareMethodResult.py
@@ -0,0 +1,162 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareMethodResult
+
+Instruments the CIM class LMI_SoftwareMethodResult
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import MethodResult
+from openlmi.software.yumdb import YumDB
+class LMI_SoftwareMethodResult(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareMethodResult
+
+ Jobs are sometimes used to represent extrinsic method invocations that
+ execute for times longer than the length of time is reasonable to
+ require a client to wait. The method executing continues beyond the
+ method return to the client. The class provides the result of the
+ execution of a Job that was itself started by the side-effect of this
+ extrinsic method invocation. The indication instances embedded an
+ instance of this class shall be the same indications delivered to
+ listening clients or recorded, all or in part, to logs. Basically,
+ this approach is a corollary to the functionality provided by an
+ instance of ListenerDestinationLog (as defined in the Interop Model).
+ The latter provides a comprehensive, persistent mechanism for
+ recording Job results, but is also more resource-intensive and
+ requires supporting logging functionality. Both the extra resources
+ and logging may not be available in all environments (for example,
+ embedded environments). Therefore, this instance-based approach is
+ also provided. The MethodResult instances shall not exist after the
+ associated ConcreteJob is deleted.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ job = MethodResult.object_path2job(model.path)
+ return MethodResult.job2model(job, keys_only=False, model=model)
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'InstanceID': None})
+
+ for job in YumDB.get_instance().get_job_list():
+ yield MethodResult.job2model(job, keys_only=keys_only, model=model)
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
diff --git a/src/software/openlmi/software/LMI_SystemSoftwareCollection.py b/src/software/openlmi/software/LMI_SystemSoftwareCollection.py
new file mode 100644
index 0000000..a392994
--- /dev/null
+++ b/src/software/openlmi/software/LMI_SystemSoftwareCollection.py
@@ -0,0 +1,173 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SystemSoftwareCollection
+
+Instruments the CIM class LMI_SystemSoftwareCollection
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import SystemCollection
+
+class LMI_SystemSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_SystemSoftwareCollection
+
+ SystemSoftwareCollection represents the general concept of a collection
+ that is scoped (or contained) by a System. It represents a Collection
+ that has meaning only in the context of a System, a Collection whose
+ elements are restricted by the definition of the System, or both of
+ these types of Collections. This meaning is explicitly described by
+ the (required) association, HostedCollection. An example of a
+ SystemSoftwareCollection is a Fibre Channel zone that collects network
+ ports, port groupings, and aliases (as required by a customer) in the
+ context of an AdminDomain. The Collection is not a part of the domain,
+ but merely an arbitrary grouping of the devices and other Collections
+ in the domain. In other words, the context of the Collection is
+ restricted to the domain, and its members are also limited by the
+ domain.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ @cmpi_logging.trace_method
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ if not 'InstanceID' in model:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing InstanceID key property")
+ if model['InstanceID'] != SystemCollection.get_path()['InstanceID']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No such instance.")
+
+ model['Caption'] = "System RPM Package Collection"
+ #model['Description'] = '' # TODO
+ #model['ElementName'] = '' # TODO
+ return model
+
+ @cmpi_logging.trace_method
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'InstanceID': None})
+
+ model['InstanceID'] = SystemCollection.get_path()["InstanceID"]
+ if keys_only is False:
+ yield self.get_instance(env, model)
+ else:
+ yield model
+
+ @cmpi_logging.trace_method
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
diff --git a/src/software/openlmi/software/__init__.py b/src/software/openlmi/software/__init__.py
index 6ff965f..ffdae3f 100644
--- a/src/software/openlmi/software/__init__.py
+++ b/src/software/openlmi/software/__init__.py
@@ -18,3 +18,8 @@
#
# Authors: Michal Minar <miminar@redhat.com>
#
+
+"""
+CIM providers for software management.
+Part of OpenLMI project.
+"""
diff --git a/src/software/openlmi/software/cimom_entry.py b/src/software/openlmi/software/cimom_entry.py
index 19bd57d..a83f2b8 100644
--- a/src/software/openlmi/software/cimom_entry.py
+++ b/src/software/openlmi/software/cimom_entry.py
@@ -25,28 +25,68 @@ Entry module for OpenLMI Software proviers.
"""
from openlmi.common import cmpi_logging
-from openlmi.software.LMI_SoftwarePackage import LMI_SoftwarePackage
-from openlmi.software.LMI_SoftwareInstalledPackage import \
- LMI_SoftwareInstalledPackage
-from openlmi.software.LMI_SoftwareFileCheck import LMI_SoftwareFileCheck
-from openlmi.software.LMI_SoftwarePackageChecks import \
- LMI_SoftwarePackageChecks
+from openlmi.software.LMI_SoftwareIdentity import LMI_SoftwareIdentity
+from openlmi.software.LMI_SystemSoftwareCollection import \
+ LMI_SystemSoftwareCollection
+from openlmi.software.LMI_HostedSoftwareCollection import \
+ LMI_HostedSoftwareCollection
+from openlmi.software.LMI_MemberOfSoftwareCollection import \
+ LMI_MemberOfSoftwareCollection
+from openlmi.software.LMI_InstalledSoftwareIdentity import \
+ LMI_InstalledSoftwareIdentity
+from openlmi.software.LMI_SoftwareIdentityResource import \
+ LMI_SoftwareIdentityResource
+from openlmi.software.LMI_ResourceForSoftwareIdentity import \
+ LMI_ResourceForSoftwareIdentity
+from openlmi.software.LMI_HostedSoftwareIdentityResource import \
+ LMI_HostedSoftwareIdentityResource
+from openlmi.software.LMI_SoftwareInstallationService import \
+ LMI_SoftwareInstallationService
+from openlmi.software.LMI_SoftwareInstallationJob import \
+ LMI_SoftwareInstallationJob
+from openlmi.software.LMI_SoftwareMethodResult import \
+ LMI_SoftwareMethodResult
+from openlmi.software.LMI_AffectedSoftwareJobElement import \
+ LMI_AffectedSoftwareJobElement
+from openlmi.software.LMI_AssociatedSoftwareJobMethodResult import \
+ LMI_AssociatedSoftwareJobMethodResult
+from openlmi.software.LMI_OwningSoftwareJobElement import \
+ LMI_OwningSoftwareJobElement
from openlmi.software.yumdb import YumDB
def get_providers(env):
+ """
+ @return mapping of provider names to corresponding provider instances.
+ """
cmpi_logging.LogManager(env)
providers = {
- "LMI_SoftwarePackage" : LMI_SoftwarePackage(env),
- "LMI_SoftwareInstalledPackage" : LMI_SoftwareInstalledPackage(env),
- "LMI_SoftwareFileCheck" : LMI_SoftwareFileCheck(env),
- "LMI_SoftwarePackageChecks" : LMI_SoftwarePackageChecks(env)
+ "LMI_SoftwareIdentity" : LMI_SoftwareIdentity(env),
+ "LMI_SystemSoftwareCollection" : LMI_SystemSoftwareCollection(env),
+ "LMI_HostedSoftwareCollection" : LMI_HostedSoftwareCollection(env),
+ "LMI_MemberOfSoftwareCollection" : LMI_MemberOfSoftwareCollection(env),
+ "LMI_InstalledSoftwareIdentity" : LMI_InstalledSoftwareIdentity(env),
+ "LMI_SoftwareIdentityResource" : LMI_SoftwareIdentityResource(env),
+ "LMI_ResourceForSoftwareIdentity" :
+ LMI_ResourceForSoftwareIdentity(env),
+ "LMI_HostedSoftwareIdentityResource" :
+ LMI_HostedSoftwareIdentityResource(env),
+ "LMI_SoftwareInstallationService" : \
+ LMI_SoftwareInstallationService(env),
+ "LMI_SoftwareInstallationJob" : LMI_SoftwareInstallationJob(env),
+ "LMI_SoftwareMethodResult" : LMI_SoftwareMethodResult(env),
+ "LMI_AffectedSoftwareJobElement" : LMI_AffectedSoftwareJobElement(env),
+ "LMI_AssociatedSoftwareJobMethodResult" : \
+ LMI_AssociatedSoftwareJobMethodResult(env),
+ "LMI_OwningSoftwareJobElement" : LMI_OwningSoftwareJobElement(env)
}
return providers
def can_unload(_env):
+ """ Says, whether providers can be unlouded. """
return True
def shutdown(_env):
- YumDB.getInstance().clean_up()
+ """ Release resources upon cleanup. """
+ YumDB.get_instance().clean_up()
diff --git a/src/software/openlmi/software/core/AffectedSoftwareJobElement.py b/src/software/openlmi/software/core/AffectedSoftwareJobElement.py
new file mode 100644
index 0000000..2530104
--- /dev/null
+++ b/src/software/openlmi/software/core/AffectedSoftwareJobElement.py
@@ -0,0 +1,205 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to AffectedSoftwareJobElement provider.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+from openlmi.software import util
+from openlmi.software.core import ComputerSystem
+from openlmi.software.core import Identity
+from openlmi.software.core import InstallationJob
+from openlmi.software.core import SystemCollection
+from openlmi.software.yumdb import jobs
+from openlmi.software.yumdb import PackageInfo
+
+class Values(object):
+ class ElementEffects(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Exclusive_Use = pywbem.Uint16(2)
+ Performance_Impact = pywbem.Uint16(3)
+ Element_Integrity = pywbem.Uint16(4)
+ Create = pywbem.Uint16(5)
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Exclusive Use',
+ 3: 'Performance Impact',
+ 4: 'Element Integrity',
+ 5: 'Create'
+ }
+
+@cmpi_logging.trace_function
+def check_path(env, op):
+ """
+ Checks, whether object path is valid.
+ @return affected object path
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be a CIMInstanceName")
+ ch = env.get_cimom_handle()
+
+ job = op['AffectingElement'] = InstallationJob.object_path2job(
+ op['AffectingElement'])
+ affected = op['AffectedElement']
+ if ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SoftwareIdentity'):
+ pkg_info = Identity.object_path2pkg(affected, kind='all')
+ if isinstance(job, jobs.YumSpecificPackageJob):
+ if isinstance(job.pkg, PackageInfo):
+ if pkg_info != job.pkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "AffectedElement does not match job's package:"
+ " \"%s\" != \"%s\"." % (pkg_info, job.pkg))
+ else:
+ flt = pkg_info.key_props
+ flt.pop('repoid', None)
+ if util.nevra2filter(job.pkg) != flt:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "AffectedElement does not match job's package:"
+ " \"%s\" != \"%s\"." % (pkg_info, job.pkg))
+ affected = Identity.pkg2model(pkg_info)
+ elif isinstance(job, jobs.YumInstallPackageFromURI):
+ if job.state == job.COMPLETED:
+ affected = Identity.pkg2model(job.result_data)
+ else:
+ # TODO: this should be somehow obtained from from downloaded
+ # package before installation
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No SoftwareIdentity is associated to given job.")
+ else:
+ cmpi_logging.logger.error("Unsupported async job: %s", job)
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No associated SoftwareIdentity.")
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SystemSoftwareCollection'):
+ SystemCollection.check_path(env, affected, "AffectedElement")
+ affected = SystemCollection.get_path()
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='Linux_ComputerSystem'):
+ ComputerSystem.check_path(env, affected, "AffectedElement")
+ affected = ComputerSystem.get_path()
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected an instance of LMI_SoftwareIdentity,"
+ " LMI_SystemSoftwareCollection or Linux_ComputerSystem.")
+ return (job, affected)
+
+@cmpi_logging.trace_function
+def job2affected_software_identity(job):
+ """
+ @return (path of SoftwareIdentity, ElementEffects array,
+ OtherElementEffectsDescriptions array)
+ """
+ effects = [Values.ElementEffects.Other]
+ descriptions = []
+ if isinstance(job, jobs.YumSpecificPackageJob):
+ if job.state == job.COMPLETED and job.result_data:
+ affected = Identity.pkg2model(job.result_data)
+ else:
+ affected = Identity.pkg2model(job.pkg)
+ if isinstance(job, jobs.YumInstallPackage):
+ descriptions.append("Installing")
+ elif isinstance(job, jobs.YumRemovePackage):
+ descriptions.append("Removing")
+ elif isinstance(job,
+ (jobs.YumUpdatePackage, jobs.YumUpdateToPackage)):
+ descriptions.append("Updating")
+ elif isinstance(job, jobs.YumCheckPackage):
+ descriptions.append("Checking")
+ else:
+ descriptions.append("Modifying")
+ cmpi_logging.logger.error("Unhandled job: %s", job)
+ elif isinstance(job, jobs.YumInstallPackageFromURI):
+ if job.state == job.COMPLETED:
+ affected = Identity.pkg2model(job.result_data)
+ descriptions.append("Installing")
+ else:
+ # TODO: this should be somehow obtained from from downloaded
+ # package before installation
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No SoftwareIdentity is associated to given job.")
+ else:
+ cmpi_logging.logger.error("Unsupported async job: %s", job)
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No associated SoftwareIdentity.")
+ return (affected, effects, descriptions)
+
+@cmpi_logging.trace_function
+def fill_model_computer_system(model, job, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = ComputerSystem.get_path()
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Other]
+ description = "Modifying software collection."
+ if isinstance(job, (jobs.YumInstallPackage,
+ jobs.YumInstallPackageFromURI)):
+ description = "Installing software package to collection."
+ elif isinstance(job, jobs.YumRemovePackage):
+ description = "Removing package from software collection."
+ elif isinstance(job, (jobs.YumUpdatePackage, jobs.YumUpdateToPackage)):
+ description = "Updating software package."
+ model["OtherElementEffectsDescriptions"] = [description]
+ return model
+
+@cmpi_logging.trace_function
+def fill_model_system_collection(model, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = SystemCollection.get_path()
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Exclusive_Use]
+ model["OtherElementEffectsDescriptions"] = [
+ "Package database is locked."
+ ]
+ return model
+
+@cmpi_logging.trace_function
+def generate_models_from_job(job, keys_only=True, model=None):
+ """
+ Generates all associations between job and affected elements.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("pkg must be an instance of PackageInfo or nevra")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_AffectedSoftwareJobElement",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_AffectedSoftwareJobElement",
+ path=model)
+ model["AffectingElement"] = InstallationJob.job2model(job)
+ (si, element_effects, element_effects_descriptions) = \
+ job2affected_software_identity(job)
+ model["AffectedElement"] = si
+ if not keys_only:
+ model["ElementEffects"] = element_effects
+ model["OtherElementEffectsDescriptions"] = \
+ element_effects_descriptions
+ yield model
+ fill_model_system_collection(model, keys_only=keys_only)
+ yield model
+ fill_model_computer_system(model, job, keys_only=keys_only)
+ yield model
+
diff --git a/src/software/openlmi/software/core/ComputerSystem.py b/src/software/openlmi/software/core/ComputerSystem.py
index 4b83d8f..48b4500 100644
--- a/src/software/openlmi/software/core/ComputerSystem.py
+++ b/src/software/openlmi/software/core/ComputerSystem.py
@@ -23,6 +23,8 @@ Just a common functionality related to associated class Linux_ComputerSystem.
import pywbem
import socket
+from openlmi.common import cmpi_logging
+
def get_path(prefix='Linux'):
"""
@return object path of Linux_ComputerSystem
@@ -34,15 +36,15 @@ def get_path(prefix='Linux'):
op["Name"] = socket.gethostname()
return op
-def check_path_property(env, op, prop_name):
+@cmpi_logging.trace_function
+def check_path(env, system, prop_name):
"""
- Checks, whether object path contains correct instance name of
- Linux_ComputerSystem corresponding to this system.
- If not, an exception is raised.
+ Checks instance name of ComputerSystem.
+ @param system instance name
+ @param prop_name name of object path
"""
- system = op[prop_name]
if not isinstance(system, pywbem.CIMInstanceName):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
"\"%s\" must be a CIMInstanceName" % prop_name)
our_system = get_path(prefix='CIM')
ch = env.get_cimom_handle()
@@ -71,3 +73,15 @@ def check_path_property(env, op, prop_name):
prop_name, our_system['Name'])
return True
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether object path contains correct instance name of
+ Linux_ComputerSystem corresponding to this system.
+ If not, an exception is raised.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
+
diff --git a/src/software/openlmi/software/core/Error.py b/src/software/openlmi/software/core/Error.py
new file mode 100644
index 0000000..210c00e
--- /dev/null
+++ b/src/software/openlmi/software/core/Error.py
@@ -0,0 +1,457 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to class CIM_Error.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import InstallationService
+
+class Values(object):
+ class ErrorSourceFormat(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ CIMObjectPath = pywbem.Uint16(2)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'CIMObjectPath'
+ }
+
+ class ErrorType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Communications_Error = pywbem.Uint16(2)
+ Quality_of_Service_Error = pywbem.Uint16(3)
+ Software_Error = pywbem.Uint16(4)
+ Hardware_Error = pywbem.Uint16(5)
+ Environmental_Error = pywbem.Uint16(6)
+ Security_Error = pywbem.Uint16(7)
+ Oversubscription_Error = pywbem.Uint16(8)
+ Unavailable_Resource_Error = pywbem.Uint16(9)
+ Unsupported_Operation_Error = pywbem.Uint16(10)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Communications Error',
+ 3 : 'Quality of Service Error',
+ 4 : 'Software Error',
+ 5 : 'Hardware Error',
+ 6 : 'Environmental Error',
+ 7 : 'Security Error',
+ 8 : 'Oversubscription Error',
+ 9 : 'Unavailable Resource Error',
+ 10 : 'Unsupported Operation Error'
+ }
+
+ class CIMStatusCode(object):
+ CIM_ERR_FAILED = pywbem.Uint32(1)
+ CIM_ERR_ACCESS_DENIED = pywbem.Uint32(2)
+ CIM_ERR_INVALID_NAMESPACE = pywbem.Uint32(3)
+ CIM_ERR_INVALID_PARAMETER = pywbem.Uint32(4)
+ CIM_ERR_INVALID_CLASS = pywbem.Uint32(5)
+ CIM_ERR_NOT_FOUND = pywbem.Uint32(6)
+ CIM_ERR_NOT_SUPPORTED = pywbem.Uint32(7)
+ CIM_ERR_CLASS_HAS_CHILDREN = pywbem.Uint32(8)
+ CIM_ERR_CLASS_HAS_INSTANCES = pywbem.Uint32(9)
+ CIM_ERR_INVALID_SUPERCLASS = pywbem.Uint32(10)
+ CIM_ERR_ALREADY_EXISTS = pywbem.Uint32(11)
+ CIM_ERR_NO_SUCH_PROPERTY = pywbem.Uint32(12)
+ CIM_ERR_TYPE_MISMATCH = pywbem.Uint32(13)
+ CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED = pywbem.Uint32(14)
+ CIM_ERR_INVALID_QUERY = pywbem.Uint32(15)
+ CIM_ERR_METHOD_NOT_AVAILABLE = pywbem.Uint32(16)
+ CIM_ERR_METHOD_NOT_FOUND = pywbem.Uint32(17)
+ CIM_ERR_UNEXPECTED_RESPONSE = pywbem.Uint32(18)
+ CIM_ERR_INVALID_RESPONSE_DESTINATION = pywbem.Uint32(19)
+ CIM_ERR_NAMESPACE_NOT_EMPTY = pywbem.Uint32(20)
+ CIM_ERR_INVALID_ENUMERATION_CONTEXT = pywbem.Uint32(21)
+ CIM_ERR_INVALID_OPERATION_TIMEOUT = pywbem.Uint32(22)
+ CIM_ERR_PULL_HAS_BEEN_ABANDONED = pywbem.Uint32(23)
+ CIM_ERR_PULL_CANNOT_BE_ABANDONED = pywbem.Uint32(24)
+ CIM_ERR_FILTERED_ENUMERATION_NOT_SUPPORTED = pywbem.Uint32(25)
+ CIM_ERR_CONTINUATION_ON_ERROR_NOT_SUPPORTED = pywbem.Uint32(26)
+ CIM_ERR_SERVER_LIMITS_EXCEEDED = pywbem.Uint32(27)
+ CIM_ERR_SERVER_IS_SHUTTING_DOWN = pywbem.Uint32(28)
+ CIM_ERR_QUERY_FEATURE_NOT_SUPPORTED = pywbem.Uint32(29)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 1 : 'CIM_ERR_FAILED',
+ 2 : 'CIM_ERR_ACCESS_DENIED',
+ 3 : 'CIM_ERR_INVALID_NAMESPACE',
+ 4 : 'CIM_ERR_INVALID_PARAMETER',
+ 5 : 'CIM_ERR_INVALID_CLASS',
+ 6 : 'CIM_ERR_NOT_FOUND',
+ 7 : 'CIM_ERR_NOT_SUPPORTED',
+ 8 : 'CIM_ERR_CLASS_HAS_CHILDREN',
+ 9 : 'CIM_ERR_CLASS_HAS_INSTANCES',
+ 10 : 'CIM_ERR_INVALID_SUPERCLASS',
+ 11 : 'CIM_ERR_ALREADY_EXISTS',
+ 12 : 'CIM_ERR_NO_SUCH_PROPERTY',
+ 13 : 'CIM_ERR_TYPE_MISMATCH',
+ 14 : 'CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED',
+ 15 : 'CIM_ERR_INVALID_QUERY',
+ 16 : 'CIM_ERR_METHOD_NOT_AVAILABLE',
+ 17 : 'CIM_ERR_METHOD_NOT_FOUND',
+ 18 : 'CIM_ERR_UNEXPECTED_RESPONSE',
+ 19 : 'CIM_ERR_INVALID_RESPONSE_DESTINATION',
+ 20 : 'CIM_ERR_NAMESPACE_NOT_EMPTY',
+ 21 : 'CIM_ERR_INVALID_ENUMERATION_CONTEXT',
+ 22 : 'CIM_ERR_INVALID_OPERATION_TIMEOUT',
+ 23 : 'CIM_ERR_PULL_HAS_BEEN_ABANDONED',
+ 24 : 'CIM_ERR_PULL_CANNOT_BE_ABANDONED',
+ 25 : 'CIM_ERR_FILTERED_ENUMERATION_NOT_SUPPORTED',
+ 26 : 'CIM_ERR_CONTINUATION_ON_ERROR_NOT_SUPPORTED',
+ 27 : 'CIM_ERR_SERVER_LIMITS_EXCEEDED',
+ 28 : 'CIM_ERR_SERVER_IS_SHUTTING_DOWN',
+ 29 : 'CIM_ERR_QUERY_FEATURE_NOT_SUPPORTED'
+ }
+
+ class PerceivedSeverity(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Information = pywbem.Uint16(2)
+ Degraded_Warning = pywbem.Uint16(3)
+ Minor = pywbem.Uint16(4)
+ Major = pywbem.Uint16(5)
+ Critical = pywbem.Uint16(6)
+ Fatal_NonRecoverable = pywbem.Uint16(7)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Information',
+ 3 : 'Degraded/Warning',
+ 4 : 'Minor',
+ 5 : 'Major',
+ 6 : 'Critical',
+ 7 : 'Fatal/NonRecoverable'
+ }
+
+ class ProbableCause(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Adapter_Card_Error = pywbem.Uint16(2)
+ Application_Subsystem_Failure = pywbem.Uint16(3)
+ Bandwidth_Reduced = pywbem.Uint16(4)
+ Connection_Establishment_Error = pywbem.Uint16(5)
+ Communications_Protocol_Error = pywbem.Uint16(6)
+ Communications_Subsystem_Failure = pywbem.Uint16(7)
+ Configuration_Customization_Error = pywbem.Uint16(8)
+ Congestion = pywbem.Uint16(9)
+ Corrupt_Data = pywbem.Uint16(10)
+ CPU_Cycles_Limit_Exceeded = pywbem.Uint16(11)
+ Dataset_Modem_Error = pywbem.Uint16(12)
+ Degraded_Signal = pywbem.Uint16(13)
+ DTE_DCE_Interface_Error = pywbem.Uint16(14)
+ Enclosure_Door_Open = pywbem.Uint16(15)
+ Equipment_Malfunction = pywbem.Uint16(16)
+ Excessive_Vibration = pywbem.Uint16(17)
+ File_Format_Error = pywbem.Uint16(18)
+ Fire_Detected = pywbem.Uint16(19)
+ Flood_Detected = pywbem.Uint16(20)
+ Framing_Error = pywbem.Uint16(21)
+ HVAC_Problem = pywbem.Uint16(22)
+ Humidity_Unacceptable = pywbem.Uint16(23)
+ I_O_Device_Error = pywbem.Uint16(24)
+ Input_Device_Error = pywbem.Uint16(25)
+ LAN_Error = pywbem.Uint16(26)
+ Non_Toxic_Leak_Detected = pywbem.Uint16(27)
+ Local_Node_Transmission_Error = pywbem.Uint16(28)
+ Loss_of_Frame = pywbem.Uint16(29)
+ Loss_of_Signal = pywbem.Uint16(30)
+ Material_Supply_Exhausted = pywbem.Uint16(31)
+ Multiplexer_Problem = pywbem.Uint16(32)
+ Out_of_Memory = pywbem.Uint16(33)
+ Output_Device_Error = pywbem.Uint16(34)
+ Performance_Degraded = pywbem.Uint16(35)
+ Power_Problem = pywbem.Uint16(36)
+ Pressure_Unacceptable = pywbem.Uint16(37)
+ Processor_Problem__Internal_Machine_Error_ = pywbem.Uint16(38)
+ Pump_Failure = pywbem.Uint16(39)
+ Queue_Size_Exceeded = pywbem.Uint16(40)
+ Receive_Failure = pywbem.Uint16(41)
+ Receiver_Failure = pywbem.Uint16(42)
+ Remote_Node_Transmission_Error = pywbem.Uint16(43)
+ Resource_at_or_Nearing_Capacity = pywbem.Uint16(44)
+ Response_Time_Excessive = pywbem.Uint16(45)
+ Retransmission_Rate_Excessive = pywbem.Uint16(46)
+ Software_Error = pywbem.Uint16(47)
+ Software_Program_Abnormally_Terminated = pywbem.Uint16(48)
+ Software_Program_Error__Incorrect_Results_ = pywbem.Uint16(49)
+ Storage_Capacity_Problem = pywbem.Uint16(50)
+ Temperature_Unacceptable = pywbem.Uint16(51)
+ Threshold_Crossed = pywbem.Uint16(52)
+ Timing_Problem = pywbem.Uint16(53)
+ Toxic_Leak_Detected = pywbem.Uint16(54)
+ Transmit_Failure = pywbem.Uint16(55)
+ Transmitter_Failure = pywbem.Uint16(56)
+ Underlying_Resource_Unavailable = pywbem.Uint16(57)
+ Version_Mismatch = pywbem.Uint16(58)
+ Previous_Alert_Cleared = pywbem.Uint16(59)
+ Login_Attempts_Failed = pywbem.Uint16(60)
+ Software_Virus_Detected = pywbem.Uint16(61)
+ Hardware_Security_Breached = pywbem.Uint16(62)
+ Denial_of_Service_Detected = pywbem.Uint16(63)
+ Security_Credential_Mismatch = pywbem.Uint16(64)
+ Unauthorized_Access = pywbem.Uint16(65)
+ Alarm_Received = pywbem.Uint16(66)
+ Loss_of_Pointer = pywbem.Uint16(67)
+ Payload_Mismatch = pywbem.Uint16(68)
+ Transmission_Error = pywbem.Uint16(69)
+ Excessive_Error_Rate = pywbem.Uint16(70)
+ Trace_Problem = pywbem.Uint16(71)
+ Element_Unavailable = pywbem.Uint16(72)
+ Element_Missing = pywbem.Uint16(73)
+ Loss_of_Multi_Frame = pywbem.Uint16(74)
+ Broadcast_Channel_Failure = pywbem.Uint16(75)
+ Invalid_Message_Received = pywbem.Uint16(76)
+ Routing_Failure = pywbem.Uint16(77)
+ Backplane_Failure = pywbem.Uint16(78)
+ Identifier_Duplication = pywbem.Uint16(79)
+ Protection_Path_Failure = pywbem.Uint16(80)
+ Sync_Loss_or_Mismatch = pywbem.Uint16(81)
+ Terminal_Problem = pywbem.Uint16(82)
+ Real_Time_Clock_Failure = pywbem.Uint16(83)
+ Antenna_Failure = pywbem.Uint16(84)
+ Battery_Charging_Failure = pywbem.Uint16(85)
+ Disk_Failure = pywbem.Uint16(86)
+ Frequency_Hopping_Failure = pywbem.Uint16(87)
+ Loss_of_Redundancy = pywbem.Uint16(88)
+ Power_Supply_Failure = pywbem.Uint16(89)
+ Signal_Quality_Problem = pywbem.Uint16(90)
+ Battery_Discharging = pywbem.Uint16(91)
+ Battery_Failure = pywbem.Uint16(92)
+ Commercial_Power_Problem = pywbem.Uint16(93)
+ Fan_Failure = pywbem.Uint16(94)
+ Engine_Failure = pywbem.Uint16(95)
+ Sensor_Failure = pywbem.Uint16(96)
+ Fuse_Failure = pywbem.Uint16(97)
+ Generator_Failure = pywbem.Uint16(98)
+ Low_Battery = pywbem.Uint16(99)
+ Low_Fuel = pywbem.Uint16(100)
+ Low_Water = pywbem.Uint16(101)
+ Explosive_Gas = pywbem.Uint16(102)
+ High_Winds = pywbem.Uint16(103)
+ Ice_Buildup = pywbem.Uint16(104)
+ Smoke = pywbem.Uint16(105)
+ Memory_Mismatch = pywbem.Uint16(106)
+ Out_of_CPU_Cycles = pywbem.Uint16(107)
+ Software_Environment_Problem = pywbem.Uint16(108)
+ Software_Download_Failure = pywbem.Uint16(109)
+ Element_Reinitialized = pywbem.Uint16(110)
+ Timeout = pywbem.Uint16(111)
+ Logging_Problems = pywbem.Uint16(112)
+ Leak_Detected = pywbem.Uint16(113)
+ Protection_Mechanism_Failure = pywbem.Uint16(114)
+ Protecting_Resource_Failure = pywbem.Uint16(115)
+ Database_Inconsistency = pywbem.Uint16(116)
+ Authentication_Failure = pywbem.Uint16(117)
+ Breach_of_Confidentiality = pywbem.Uint16(118)
+ Cable_Tamper = pywbem.Uint16(119)
+ Delayed_Information = pywbem.Uint16(120)
+ Duplicate_Information = pywbem.Uint16(121)
+ Information_Missing = pywbem.Uint16(122)
+ Information_Modification = pywbem.Uint16(123)
+ Information_Out_of_Sequence = pywbem.Uint16(124)
+ Key_Expired = pywbem.Uint16(125)
+ Non_Repudiation_Failure = pywbem.Uint16(126)
+ Out_of_Hours_Activity = pywbem.Uint16(127)
+ Out_of_Service = pywbem.Uint16(128)
+ Procedural_Error = pywbem.Uint16(129)
+ Unexpected_Information = pywbem.Uint16(130)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Adapter/Card Error',
+ 3 : 'Application Subsystem Failure',
+ 4 : 'Bandwidth Reduced',
+ 5 : 'Connection Establishment Error',
+ 6 : 'Communications Protocol Error',
+ 7 : 'Communications Subsystem Failure',
+ 8 : 'Configuration/Customization Error',
+ 9 : 'Congestion',
+ 10 : 'Corrupt Data',
+ 11 : 'CPU Cycles Limit Exceeded',
+ 12 : 'Dataset/Modem Error',
+ 13 : 'Degraded Signal',
+ 14 : 'DTE-DCE Interface Error',
+ 15 : 'Enclosure Door Open',
+ 16 : 'Equipment Malfunction',
+ 17 : 'Excessive Vibration',
+ 18 : 'File Format Error',
+ 19 : 'Fire Detected',
+ 20 : 'Flood Detected',
+ 21 : 'Framing Error',
+ 22 : 'HVAC Problem',
+ 23 : 'Humidity Unacceptable',
+ 24 : 'I/O Device Error',
+ 25 : 'Input Device Error',
+ 26 : 'LAN Error',
+ 27 : 'Non-Toxic Leak Detected',
+ 28 : 'Local Node Transmission Error',
+ 29 : 'Loss of Frame',
+ 30 : 'Loss of Signal',
+ 31 : 'Material Supply Exhausted',
+ 32 : 'Multiplexer Problem',
+ 33 : 'Out of Memory',
+ 34 : 'Output Device Error',
+ 35 : 'Performance Degraded',
+ 36 : 'Power Problem',
+ 37 : 'Pressure Unacceptable',
+ 38 : 'Processor Problem (Internal Machine Error)',
+ 39 : 'Pump Failure',
+ 40 : 'Queue Size Exceeded',
+ 41 : 'Receive Failure',
+ 42 : 'Receiver Failure',
+ 43 : 'Remote Node Transmission Error',
+ 44 : 'Resource at or Nearing Capacity',
+ 45 : 'Response Time Excessive',
+ 46 : 'Retransmission Rate Excessive',
+ 47 : 'Software Error',
+ 48 : 'Software Program Abnormally Terminated',
+ 49 : 'Software Program Error (Incorrect Results)',
+ 50 : 'Storage Capacity Problem',
+ 51 : 'Temperature Unacceptable',
+ 52 : 'Threshold Crossed',
+ 53 : 'Timing Problem',
+ 54 : 'Toxic Leak Detected',
+ 55 : 'Transmit Failure',
+ 56 : 'Transmitter Failure',
+ 57 : 'Underlying Resource Unavailable',
+ 58 : 'Version Mismatch',
+ 59 : 'Previous Alert Cleared',
+ 60 : 'Login Attempts Failed',
+ 61 : 'Software Virus Detected',
+ 62 : 'Hardware Security Breached',
+ 63 : 'Denial of Service Detected',
+ 64 : 'Security Credential Mismatch',
+ 65 : 'Unauthorized Access',
+ 66 : 'Alarm Received',
+ 67 : 'Loss of Pointer',
+ 68 : 'Payload Mismatch',
+ 69 : 'Transmission Error',
+ 70 : 'Excessive Error Rate',
+ 71 : 'Trace Problem',
+ 72 : 'Element Unavailable',
+ 73 : 'Element Missing',
+ 74 : 'Loss of Multi Frame',
+ 75 : 'Broadcast Channel Failure',
+ 76 : 'Invalid Message Received',
+ 77 : 'Routing Failure',
+ 78 : 'Backplane Failure',
+ 79 : 'Identifier Duplication',
+ 80 : 'Protection Path Failure',
+ 81 : 'Sync Loss or Mismatch',
+ 82 : 'Terminal Problem',
+ 83 : 'Real Time Clock Failure',
+ 84 : 'Antenna Failure',
+ 85 : 'Battery Charging Failure',
+ 86 : 'Disk Failure',
+ 87 : 'Frequency Hopping Failure',
+ 88 : 'Loss of Redundancy',
+ 89 : 'Power Supply Failure',
+ 90 : 'Signal Quality Problem',
+ 91 : 'Battery Discharging',
+ 92 : 'Battery Failure',
+ 93 : 'Commercial Power Problem',
+ 94 : 'Fan Failure',
+ 95 : 'Engine Failure',
+ 96 : 'Sensor Failure',
+ 97 : 'Fuse Failure',
+ 98 : 'Generator Failure',
+ 99 : 'Low Battery',
+ 100 : 'Low Fuel',
+ 101 : 'Low Water',
+ 102 : 'Explosive Gas',
+ 103 : 'High Winds',
+ 104 : 'Ice Buildup',
+ 105 : 'Smoke',
+ 106 : 'Memory Mismatch',
+ 107 : 'Out of CPU Cycles',
+ 108 : 'Software Environment Problem',
+ 109 : 'Software Download Failure',
+ 110 : 'Element Reinitialized',
+ 111 : 'Timeout',
+ 112 : 'Logging Problems',
+ 113 : 'Leak Detected',
+ 114 : 'Protection Mechanism Failure',
+ 115 : 'Protecting Resource Failure',
+ 116 : 'Database Inconsistency',
+ 117 : 'Authentication Failure',
+ 118 : 'Breach of Confidentiality',
+ 119 : 'Cable Tamper',
+ 120 : 'Delayed Information',
+ 121 : 'Duplicate Information',
+ 122 : 'Information Missing',
+ 123 : 'Information Modification',
+ 124 : 'Information Out of Sequence',
+ 125 : 'Key Expired',
+ 126 : 'Non-Repudiation Failure',
+ 127 : 'Out of Hours Activity',
+ 128 : 'Out of Service',
+ 129 : 'Procedural Error',
+ 130 : 'Unexpected Information'
+ }
+
+@cmpi_logging.trace_function
+def make_instance(
+ status_code=Values.CIMStatusCode.CIM_ERR_FAILED,
+ error_type=Values.ErrorType.Software_Error,
+ probable_cause=Values.ErrorType.Unknown,
+ error_source=None,
+ status_code_description=None,
+ message=None,
+ message_arguments=None,
+ probable_cause_description=None,
+ recommended_actions=None):
+ for param in ('status_code', 'probable_cause', 'error_type'):
+ if not isinstance(locals()[param], (int, long)):
+ raise TypeError('%s must be integer'%param)
+ if error_source is None:
+ error_source = InstallationService.get_path()
+ if not isinstance(error_source, pywbem.CIMInstanceName):
+ raise TypeError('error_source must be a CIMInstanceName')
+
+ inst = pywbem.CIMInstance(classname="CIM_Error",
+ path=pywbem.CIMInstanceName(classname="CIM_Error",
+ namespace="root/cimv2"))
+ inst['CIMStatusCode'] = pywbem.Uint32(status_code)
+ if status_code_description is not None:
+ inst['CIMStatusCodeDescription'] = status_code_description
+ inst['ErrorType'] = pywbem.Uint16(error_type)
+ inst['ErrorSource'] = str(error_source)
+ inst['ErrorSourceFormat'] = Values.ErrorSourceFormat.CIMObjectPath
+ if message is not None:
+ inst['Message'] = message
+ if message_arguments is not None:
+ inst['MessageArguments'] = message_arguments
+ inst['ProbableCause'] = pywbem.Uint16(probable_cause)
+ #inst['PerceivedSeverity'] = pywbem.Uint16(perceived_severity)
+ if probable_cause_description is not None:
+ inst['ProbableCauseDescription'] = probable_cause_description
+ if recommended_actions is not None:
+ inst['RecommendedActions'] = recommended_actions
+ return inst
+
diff --git a/src/software/openlmi/software/core/Identity.py b/src/software/openlmi/software/core/Identity.py
new file mode 100644
index 0000000..eda0ab9
--- /dev/null
+++ b/src/software/openlmi/software/core/Identity.py
@@ -0,0 +1,270 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to LMI_SoftwareIdentity provider.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+from openlmi.software import util
+from openlmi.software.yumdb import PackageInfo, YumDB
+
+class Values(object):
+ class DetailedStatus(object):
+ Not_Available = pywbem.Uint16(0)
+ No_Additional_Information = pywbem.Uint16(1)
+ Stressed = pywbem.Uint16(2)
+ Predictive_Failure = pywbem.Uint16(3)
+ Non_Recoverable_Error = pywbem.Uint16(4)
+ Supporting_Entity_in_Error = pywbem.Uint16(5)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class Status(object):
+ OK = 'OK'
+ Error = 'Error'
+ Degraded = 'Degraded'
+ Unknown = 'Unknown'
+ Pred_Fail = 'Pred Fail'
+ Starting = 'Starting'
+ Stopping = 'Stopping'
+ Service = 'Service'
+ Stressed = 'Stressed'
+ NonRecover = 'NonRecover'
+ No_Contact = 'No Contact'
+ Lost_Comm = 'Lost Comm'
+ Stopped = 'Stopped'
+
+ class HealthState(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(5)
+ Degraded_Warning = pywbem.Uint16(10)
+ Minor_failure = pywbem.Uint16(15)
+ Major_failure = pywbem.Uint16(20)
+ Critical_failure = pywbem.Uint16(25)
+ Non_recoverable_error = pywbem.Uint16(30)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class Classifications(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Driver = pywbem.Uint16(2)
+ Configuration_Software = pywbem.Uint16(3)
+ Application_Software = pywbem.Uint16(4)
+ Instrumentation = pywbem.Uint16(5)
+ Firmware_BIOS = pywbem.Uint16(6)
+ Diagnostic_Software = pywbem.Uint16(7)
+ Operating_System = pywbem.Uint16(8)
+ Middleware = pywbem.Uint16(9)
+ Firmware = pywbem.Uint16(10)
+ BIOS_FCode = pywbem.Uint16(11)
+ Support_Service_Pack = pywbem.Uint16(12)
+ Software_Bundle = pywbem.Uint16(13)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+
+ class ExtendedResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Not_Applicable = pywbem.Uint16(2)
+ Linux_RPM = pywbem.Uint16(3)
+ HP_UX_Depot = pywbem.Uint16(4)
+ Windows_MSI = pywbem.Uint16(5)
+ Solaris_Package = pywbem.Uint16(6)
+ Macintosh_Disk_Image = pywbem.Uint16(7)
+ Debian_linux_Package = pywbem.Uint16(8)
+ VMware_vSphere_Installation_Bundle = pywbem.Uint16(9)
+ VMware_Software_Bulletin = pywbem.Uint16(10)
+ HP_Smart_Component = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class CommunicationStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Communication_OK = pywbem.Uint16(2)
+ Lost_Communication = pywbem.Uint16(3)
+ No_Contact = pywbem.Uint16(4)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class OperationalStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ OK = pywbem.Uint16(2)
+ Degraded = pywbem.Uint16(3)
+ Stressed = pywbem.Uint16(4)
+ Predictive_Failure = pywbem.Uint16(5)
+ Error = pywbem.Uint16(6)
+ Non_Recoverable_Error = pywbem.Uint16(7)
+ Starting = pywbem.Uint16(8)
+ Stopping = pywbem.Uint16(9)
+ Stopped = pywbem.Uint16(10)
+ In_Service = pywbem.Uint16(11)
+ No_Contact = pywbem.Uint16(12)
+ Lost_Communication = pywbem.Uint16(13)
+ Aborted = pywbem.Uint16(14)
+ Dormant = pywbem.Uint16(15)
+ Supporting_Entity_in_Error = pywbem.Uint16(16)
+ Completed = pywbem.Uint16(17)
+ Power_Mode = pywbem.Uint16(18)
+ Relocating = pywbem.Uint16(19)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class OperatingStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Servicing = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Stopping = pywbem.Uint16(4)
+ Stopped = pywbem.Uint16(5)
+ Aborted = pywbem.Uint16(6)
+ Dormant = pywbem.Uint16(7)
+ Completed = pywbem.Uint16(8)
+ Migrating = pywbem.Uint16(9)
+ Emigrating = pywbem.Uint16(10)
+ Immigrating = pywbem.Uint16(11)
+ Snapshotting = pywbem.Uint16(12)
+ Shutting_Down = pywbem.Uint16(13)
+ In_Test = pywbem.Uint16(14)
+ Transitioning = pywbem.Uint16(15)
+ In_Service = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+
+@cmpi_logging.trace_function
+def object_path2nevra(op, with_epoch='NOT_ZERO'):
+ """Get nevra out of object path. Also checks for validity."""
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if (not "InstanceID" in op or not op['InstanceID']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, "Wrong keys.")
+ instid = op['InstanceID']
+ if not instid.lower().startswith("lmi:softwareidentity:"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "InstanceID must start with LMI:SoftwareIdentity: prefix.")
+ instid = instid[len("LMI:SoftwareIdentity:"):]
+ match = util.RE_NEVRA_OPT_EPOCH.match(instid)
+ if not match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Wrong InstanceID. Expected valid nevra"
+ ' (name-[epoch:]version-release.arch): "%s".' %
+ instid)
+ epoch = match.group('epoch')
+ if not epoch:
+ epoch = "0"
+ return util.make_nevra(
+ match.group('name'),
+ epoch,
+ match.group('version'),
+ match.group('release'),
+ match.group('arch'), with_epoch=with_epoch)
+
+@cmpi_logging.trace_function
+def object_path2pkg(op,
+ kind='installed',
+ include_repos=None,
+ exclude_repos=None,
+ repoid=None,
+ return_all=False):
+ """
+ @param op must contain precise information of package,
+ otherwise an error is raised
+ @param kind one of yumdb.jobs.YumGetPackageList.SUPPORTED_KINDS
+ says, where to look for given package
+ @param repoid if not None, specifies repoid filter on package;
+ note, that this does not make sure, that repoid will be enabled.
+ @param return_all if True, return list of matching packages as returned
+ by YumDB.filter_packages(), otherwise single package is returned
+ """
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+
+ pkglist = YumDB.get_instance().filter_packages(kind,
+ allow_duplicates=kind not in ('installed', 'avail_reinst'),
+ include_repos=include_repos,
+ exclude_repos=exclude_repos,
+ repoid=repoid,
+ nevra=object_path2nevra(op, 'ALWAYS'))
+ if return_all is True:
+ return pkglist
+ if len(pkglist) > 0:
+ return pkglist[0]
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No matching package found for InstanceID=\"%s\".' %
+ op["InstanceID"])
+
+@cmpi_logging.trace_function
+def pkg2model(pkg, keys_only=True, model=None):
+ """
+ @param pkg can either be an instance of PackageInfo or nevra as string
+ @param model if not None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ if not isinstance(pkg, (basestring, PackageInfo)):
+ raise TypeError("pkg must be an instance of PackageInfo or nevra")
+ if isinstance(pkg, basestring) and not keys_only:
+ raise ValueError("can not create instance out of nevra")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareIdentity", path=model)
+ nevra = pkg if isinstance(pkg, basestring) else pkg.nevra
+ model['InstanceID'] = 'LMI:SoftwareIdentity:'+nevra
+ if not keys_only:
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ model['Caption'] = pkg.summary
+ model['Classifications'] = [pywbem.Uint16(0)]
+ model['Description'] = pkg.description
+ model['ElementName'] = pkg.nevra
+ if pkg.installed:
+ model['InstallDate'] = pywbem.CIMDateTime(pkg.install_time)
+ else:
+ model['InstallDate'] = pywbem.CIMProperty(
+ 'InstallDate', None, type='datetime')
+ model['IsEntity'] = True
+ model['Name'] = pkg.name
+ try:
+ model["Epoch"] = pywbem.Uint32(int(pkg.epoch))
+ except ValueError:
+ cmpi_logging.logger.error('Could not convert epoch "%s"'
+ ' to integer for package \"%s\"!' % (pkg.epoch, pkg))
+ model["Epoch"] = pywbem.CIMProperty('Epoch', None, type='uint32')
+ model['Version'] = pkg.version
+ model['Release'] = pkg.release
+ model['Architecture'] = pkg.arch
+ model['TargetTypes'] = ['rpm', 'yum']
+ model['VersionString'] = pkg.evra
+ return model
+
diff --git a/src/software/openlmi/software/core/IdentityResource.py b/src/software/openlmi/software/core/IdentityResource.py
new file mode 100644
index 0000000..579b0f1
--- /dev/null
+++ b/src/software/openlmi/software/core/IdentityResource.py
@@ -0,0 +1,650 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Just a common functionality related to LMI_SoftwareIdentityResource provider.
+"""
+
+import pywbem
+import socket
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import ComputerSystem
+from openlmi.software.yumdb import YumDB
+from openlmi.software.yumdb.repository import Repository
+
+class Values(object):
+ class DetailedStatus(object):
+ Not_Available = pywbem.Uint16(0)
+ No_Additional_Information = pywbem.Uint16(1)
+ Stressed = pywbem.Uint16(2)
+ Predictive_Failure = pywbem.Uint16(3)
+ Non_Recoverable_Error = pywbem.Uint16(4)
+ Supporting_Entity_in_Error = pywbem.Uint16(5)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Not Available',
+ 1 : 'No Additional Information',
+ 2 : 'Stressed',
+ 3 : 'Predictive Failure',
+ 4 : 'Non-Recoverable Error',
+ 5 : 'Supporting Entity in Error'
+ }
+
+ class RequestedState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 5 : 'No Change',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Deferred',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset',
+ 12 : 'Not Applicable'
+ }
+
+ class HealthState(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(5)
+ Degraded_Warning = pywbem.Uint16(10)
+ Minor_failure = pywbem.Uint16(15)
+ Major_failure = pywbem.Uint16(20)
+ Critical_failure = pywbem.Uint16(25)
+ Non_recoverable_error = pywbem.Uint16(30)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 5 : 'OK',
+ 10 : 'Degraded/Warning',
+ 15 : 'Minor failure',
+ 20 : 'Major failure',
+ 25 : 'Critical failure',
+ 30 : 'Non-recoverable error'
+ }
+
+ class TransitioningToState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 5 : 'No Change',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Defer',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset',
+ 12 : 'Not Applicable'
+ }
+
+ class ResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Installer_and_Payload = pywbem.Uint16(2)
+ Installer = pywbem.Uint16(3)
+ Payload = pywbem.Uint16(4)
+ Installability_checker = pywbem.Uint16(5)
+ Security_Advisory = pywbem.Uint16(6)
+ Engineering_Advisory = pywbem.Uint16(7)
+ Technical_release_notes = pywbem.Uint16(9)
+ Change_notification = pywbem.Uint16(10)
+ Whitepaper = pywbem.Uint16(11)
+ Marketing_Documentation = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Installer and Payload',
+ 3 : 'Installer',
+ 4 : 'Payload',
+ 5 : 'Installability checker',
+ 6 : 'Security Advisory',
+ 7 : 'Engineering Advisory',
+ 9 : 'Technical release notes',
+ 10 : 'Change notification',
+ 11 : 'Whitepaper',
+ 12 : 'Marketing Documentation'
+ }
+
+ class EnabledState(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shutting_Down = pywbem.Uint16(4)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ In_Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Starting = pywbem.Uint16(10)
+ # DMTF_Reserved = 11..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shutting Down',
+ 5 : 'Not Applicable',
+ 6 : 'Enabled but Offline',
+ 7 : 'In Test',
+ 8 : 'Deferred',
+ 9 : 'Quiesce',
+ 10 : 'Starting'
+ }
+
+ class ExtendedResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Applicable = pywbem.Uint16(2)
+ Linux_RPM = pywbem.Uint16(3)
+ HP_UX_Depot = pywbem.Uint16(4)
+ Windows_MSI = pywbem.Uint16(5)
+ Solaris_Package = pywbem.Uint16(6)
+ Macintosh_Disk_Image = pywbem.Uint16(7)
+ Debian_linux_Package = pywbem.Uint16(8)
+ HP_Smart_Component = pywbem.Uint16(11)
+ # Vendor_Reserved = 101..200
+ HTML = pywbem.Uint16(201)
+ PDF = pywbem.Uint16(202)
+ Text_File = pywbem.Uint16(203)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Not Applicable',
+ 3 : 'Linux RPM',
+ 4 : 'HP-UX Depot',
+ 5 : 'Windows MSI',
+ 6 : 'Solaris Package',
+ 7 : 'Macintosh Disk Image',
+ 8 : 'Debian linux Package',
+ 201 : 'HTML',
+ 202 : 'PDF',
+ 11 : 'HP Smart Component',
+ 203 : 'Text File'
+ }
+
+ class AccessContext(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Default_Gateway = pywbem.Uint16(2)
+ DNS_Server = pywbem.Uint16(3)
+ SNMP_Trap_Destination = pywbem.Uint16(4)
+ MPLS_Tunnel_Destination = pywbem.Uint16(5)
+ DHCP_Server = pywbem.Uint16(6)
+ SMTP_Server = pywbem.Uint16(7)
+ LDAP_Server = pywbem.Uint16(8)
+ Network_Time_Protocol__NTP__Server = pywbem.Uint16(9)
+ Management_Service = pywbem.Uint16(10)
+ internet_Storage_Name_Service__iSNS_ = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Default Gateway',
+ 3 : 'DNS Server',
+ 4 : 'SNMP Trap Destination',
+ 5 : 'MPLS Tunnel Destination',
+ 6 : 'DHCP Server',
+ 7 : 'SMTP Server',
+ 8 : 'LDAP Server',
+ 9 : 'Network Time Protocol (NTP) Server',
+ 10 : 'Management Service',
+ 11 : 'internet Storage Name Service (iSNS)'
+ }
+
+ class AvailableRequestedStates(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Defer',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset'
+ }
+
+ class Status(object):
+ OK = 'OK'
+ Error = 'Error'
+ Degraded = 'Degraded'
+ Unknown = 'Unknown'
+ Pred_Fail = 'Pred Fail'
+ Starting = 'Starting'
+ Stopping = 'Stopping'
+ Service = 'Service'
+ Stressed = 'Stressed'
+ NonRecover = 'NonRecover'
+ No_Contact = 'No Contact'
+ Lost_Comm = 'Lost Comm'
+ Stopped = 'Stopped'
+
+ class CommunicationStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Communication_OK = pywbem.Uint16(2)
+ Lost_Communication = pywbem.Uint16(3)
+ No_Contact = pywbem.Uint16(4)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Not Available',
+ 2 : 'Communication OK',
+ 3 : 'Lost Communication',
+ 4 : 'No Contact'
+ }
+
+ class OperationalStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ OK = pywbem.Uint16(2)
+ Degraded = pywbem.Uint16(3)
+ Stressed = pywbem.Uint16(4)
+ Predictive_Failure = pywbem.Uint16(5)
+ Error = pywbem.Uint16(6)
+ Non_Recoverable_Error = pywbem.Uint16(7)
+ Starting = pywbem.Uint16(8)
+ Stopping = pywbem.Uint16(9)
+ Stopped = pywbem.Uint16(10)
+ In_Service = pywbem.Uint16(11)
+ No_Contact = pywbem.Uint16(12)
+ Lost_Communication = pywbem.Uint16(13)
+ Aborted = pywbem.Uint16(14)
+ Dormant = pywbem.Uint16(15)
+ Supporting_Entity_in_Error = pywbem.Uint16(16)
+ Completed = pywbem.Uint16(17)
+ Power_Mode = pywbem.Uint16(18)
+ Relocating = pywbem.Uint16(19)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'OK',
+ 3 : 'Degraded',
+ 4 : 'Stressed',
+ 5 : 'Predictive Failure',
+ 6 : 'Error',
+ 7 : 'Non-Recoverable Error',
+ 8 : 'Starting',
+ 9 : 'Stopping',
+ 10 : 'Stopped',
+ 11 : 'In Service',
+ 12 : 'No Contact',
+ 13 : 'Lost Communication',
+ 14 : 'Aborted',
+ 15 : 'Dormant',
+ 16 : 'Supporting Entity in Error',
+ 17 : 'Completed',
+ 18 : 'Power Mode',
+ 19 : 'Relocating'
+ }
+
+ class OperatingStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Servicing = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Stopping = pywbem.Uint16(4)
+ Stopped = pywbem.Uint16(5)
+ Aborted = pywbem.Uint16(6)
+ Dormant = pywbem.Uint16(7)
+ Completed = pywbem.Uint16(8)
+ Migrating = pywbem.Uint16(9)
+ Emigrating = pywbem.Uint16(10)
+ Immigrating = pywbem.Uint16(11)
+ Snapshotting = pywbem.Uint16(12)
+ Shutting_Down = pywbem.Uint16(13)
+ In_Test = pywbem.Uint16(14)
+ Transitioning = pywbem.Uint16(15)
+ In_Service = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Not Available',
+ 2 : 'Servicing',
+ 3 : 'Starting',
+ 4 : 'Stopping',
+ 5 : 'Stopped',
+ 6 : 'Aborted',
+ 7 : 'Dormant',
+ 8 : 'Completed',
+ 9 : 'Migrating',
+ 10 : 'Emigrating',
+ 11 : 'Immigrating',
+ 12 : 'Snapshotting',
+ 13 : 'Shutting Down',
+ 14 : 'In Test',
+ 15 : 'Transitioning',
+ 16 : 'In Service'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_or_Unspecified_Error = pywbem.Uint32(2)
+ Cannot_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+
+ class EnabledDefault(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ No_Default = pywbem.Uint16(7)
+ Quiesce = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 5 : 'Not Applicable',
+ 6 : 'Enabled but Offline',
+ 7 : 'No Default',
+ 9 : 'Quiesce'
+ }
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'OK',
+ 2 : 'Degraded',
+ 3 : 'Error'
+ }
+
+ class InfoFormat(object):
+ Other = pywbem.Uint16(1)
+ Host_Name = pywbem.Uint16(2)
+ IPv4_Address = pywbem.Uint16(3)
+ IPv6_Address = pywbem.Uint16(4)
+ IPX_Address = pywbem.Uint16(5)
+ DECnet_Address = pywbem.Uint16(6)
+ SNA_Address = pywbem.Uint16(7)
+ Autonomous_System_Number = pywbem.Uint16(8)
+ MPLS_Label = pywbem.Uint16(9)
+ IPv4_Subnet_Address = pywbem.Uint16(10)
+ IPv6_Subnet_Address = pywbem.Uint16(11)
+ IPv4_Address_Range = pywbem.Uint16(12)
+ IPv6_Address_Range = pywbem.Uint16(13)
+ Dial_String = pywbem.Uint16(100)
+ Ethernet_Address = pywbem.Uint16(101)
+ Token_Ring_Address = pywbem.Uint16(102)
+ ATM_Address = pywbem.Uint16(103)
+ Frame_Relay_Address = pywbem.Uint16(104)
+ URL = pywbem.Uint16(200)
+ FQDN = pywbem.Uint16(201)
+ User_FQDN = pywbem.Uint16(202)
+ DER_ASN1_DN = pywbem.Uint16(203)
+ DER_ASN1_GN = pywbem.Uint16(204)
+ Key_ID = pywbem.Uint16(205)
+ Parameterized_URL = pywbem.Uint16(206)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 1 : 'Other',
+ 2 : 'Host Name',
+ 3 : 'IPv4 Address',
+ 4 : 'IPv6 Address',
+ 5 : 'IPX Address',
+ 6 : 'DECnet Address',
+ 7 : 'SNA Address',
+ 8 : 'Autonomous System Number',
+ 9 : 'MPLS Label',
+ 10 : 'IPv4 Subnet Address',
+ 11 : 'IPv6 Subnet Address',
+ 12 : 'IPv4 Address Range',
+ 13 : 'IPv6 Address Range',
+ 200 : 'URL',
+ 201 : 'FQDN',
+ 202 : 'User FQDN',
+ 203 : 'DER ASN1 DN',
+ 204 : 'DER ASN1 GN',
+ 205 : 'Key ID',
+ 206 : 'Parameterized URL',
+ 100 : 'Dial String',
+ 101 : 'Ethernet Address',
+ 102 : 'Token Ring Address',
+ 103 : 'ATM Address',
+ 104 : 'Frame Relay Address'
+ }
+
+@cmpi_logging.trace_function
+def object_path2repo(env, op, kind='enabled'):
+ """
+ @param op must contain precise information of repository,
+ otherwise an error is raised
+ """
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if ( (not "CreationClassName" in op or not op['CreationClassName'])
+ or (not "Name" in op or not op["Name"])
+ or ( not "SystemCreationClassName" in op
+ or not op["SystemCreationClassName"])
+ or (not "SystemName" in op or not op["SystemName"])):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, "Wrong keys.")
+ if op["SystemName"] != socket.gethostname():
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'SystemName "%s" does not match "%s".' % (
+ op["SystemName"], socket.gethostname()))
+ ch = env.get_cimom_handle()
+ if not ch.is_subclass("root/cimv2",
+ sub=op["CreationClassName"],
+ super="CIM_SoftwareIdentityResource"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'CreationClassName \"%s\" must be a subclass of "%s".' % (
+ op["CreationClassName"], "CIM_SoftwareIdentityResource"))
+ if not ch.is_subclass("root/cimv2",
+ sub=op["SystemCreationClassName"],
+ super="CIM_ComputerSystem"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'SystemCreationClassName of \"%s\" must be a subclass of "%s".'
+ % (op["CreationClassName"], "CIM_SoftwareIdentityResource"))
+ repos = YumDB.get_instance().filter_repositories(kind, repoid=op["Name"])
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No matching repository found for Name=\"%s\".' % op["Name"])
+ return repos[0]
+
+@cmpi_logging.trace_function
+def _fill_non_keys(repo, model):
+ """
+ Fills into the model of instance all non-key properties.
+ """
+ model['AccessContext'] = Values.AccessContext.Other
+ if repo.mirror_list:
+ access_info = repo.mirror_list
+ elif repo.base_urls:
+ if len(repo.base_urls) > 0:
+ if len(repo.base_urls) > 1:
+ cmpi_logging.logger.warn(
+ 'multiple base urls found for repository "%s", selecting'
+ ' the last one', repo)
+ access_info = repo.base_urls[-1]
+ else:
+ cmpi_logging.logger.error(
+ 'no base url found for repository "%s"' % repo)
+ access_info = pywbem.CIMProperty('AccessInfo',
+ None, type='string')
+ model["AccessInfo"] = access_info
+ model['AvailableRequestedStates'] = [
+ Values.AvailableRequestedStates.Enabled,
+ Values.AvailableRequestedStates.Disabled]
+ model['Caption'] = repo.name
+ model['Cost'] = pywbem.Sint32(repo.cost)
+ model['Description'] = "[%s] - %s for %s architecture with cost %d" % (
+ repo.repoid, repo.name, repo.basearch, repo.cost)
+ model['ElementName'] = repo.repoid
+ model['EnabledDefault'] = Values.EnabledDefault.Not_Applicable
+ if repo.enabled:
+ model['EnabledState'] = Values.EnabledState.Enabled
+ else:
+ model['EnabledState'] = Values.EnabledState.Disabled
+ model['ExtendedResourceType'] = Values.ExtendedResourceType.Linux_RPM
+ model['GPGCheck'] = repo.gpg_check
+ if repo.ready:
+ model['HealthState'] = Values.HealthState.OK
+ else:
+ model['HealthState'] = Values.HealthState.Major_failure
+ if repo.revision is not None:
+ model["Generation"] = pywbem.Uint64(repo.revision)
+ else:
+ model['Generation'] = pywbem.CIMProperty('Generation',
+ None, type='uint64')
+ model['InfoFormat'] = Values.InfoFormat.URL
+ model['InstanceID'] = 'LMI:SoftwareIdentityResource:' + repo.repoid
+ if repo.mirror_list:
+ model["MirrorList"] = repo.mirror_list
+ else:
+ model['MirrorList'] = pywbem.CIMProperty('MirrorList',
+ None, type='string')
+ model['OperationalStatus'] = [ Values.OperationalStatus.OK
+ if repo.ready else Values.OperationalStatus.Error]
+ model['OtherAccessContext'] = "YUM package repository"
+ model['OtherResourceType'] = "RPM Software Package"
+ # this would need to populateSack, which is expensive
+ #model["PackageCount"] = pywbem.Uint32(repo.pkg_count)
+ if repo.ready:
+ model['PrimaryStatus'] = Values.PrimaryStatus.OK
+ else:
+ model['PrimaryStatus'] = Values.PrimaryStatus.Error
+ model['RepoGPGCheck'] = repo.repo_gpg_check
+ if repo.enabled:
+ model['RequestedState'] = Values.RequestedState.Enabled
+ else:
+ model['RequestedState'] = Values.RequestedState.Disabled
+ model['ResourceType'] = Values.ResourceType.Other
+ model['StatusDescriptions'] = [ "Ready" if repo.ready else "Not Ready" ]
+ model['TimeOfLastStateChange'] = pywbem.CIMDateTime(repo.last_edit)
+ if repo.last_update is not None:
+ model['TimeOfLastUpdate'] = pywbem.CIMDateTime(repo.last_update)
+ else:
+ model['TimeOfLastUpdate'] = pywbem.CIMProperty('TimeOfLastUpdate',
+ None, type='datetime')
+ model['TransitioningToState'] = Values.TransitioningToState.Not_Applicable
+
+@cmpi_logging.trace_function
+def repo2model(repo, keys_only=True, model=None):
+ """
+ @param model if not None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ if not isinstance(repo, Repository):
+ raise TypeError("pkg must be an instance of Repository")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareIdentityResource",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance(
+ "LMI_SoftwareIdentityResource", path=model)
+ if isinstance(model, pywbem.CIMInstance):
+ def _set_key(k, value):
+ """Sets the value of key property of cim instance"""
+ model[k] = value
+ model.path[k] = value #pylint: disable=E1103
+ else:
+ _set_key = model.__setitem__
+ _set_key('CreationClassName', "LMI_SoftwareIdentityResource")
+ _set_key("Name", repo.repoid)
+ _set_key("SystemCreationClassName", "Linux_ComputerSystem")
+ _set_key("SystemName", ComputerSystem.get_path()["Name"])
+ if not keys_only:
+ _fill_non_keys(repo, model)
+
+ return model
+
diff --git a/src/software/openlmi/software/core/InstallationJob.py b/src/software/openlmi/software/core/InstallationJob.py
new file mode 100644
index 0000000..2ac1f10
--- /dev/null
+++ b/src/software/openlmi/software/core/InstallationJob.py
@@ -0,0 +1,610 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to SoftwareInstallationJob.
+"""
+
+from datetime import datetime, timedelta
+import pywbem
+import time
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import Error
+from openlmi.software.yumdb import errors, jobs
+from openlmi.software.yumdb import YumDB
+
+JOB_DESCRIPTIONS = {
+ jobs.YumInstallPackage :
+ 'Software package installation job %(jobid)d for "%(pkg)s".',
+ jobs.YumRemovePackage :
+ 'Software package removal job %(jobid)d for "%(pkg)s".',
+ jobs.YumUpdateToPackage :
+ 'Software package update job %(jobid)d for "%(pkg)s".',
+ jobs.YumUpdatePackage :
+ 'Software package update job %(jobid)d for "%(pkg)s".',
+ jobs.YumCheckPackage :
+ 'Software package check job %(jobid)d for "%(pkg)s".',
+ jobs.YumInstallPackageFromURI :
+ 'Software package installation job %(jobid)d from uri: "%(uri)s".',
+}
+
+class Values(object):
+ class DetailedStatus(object):
+ Not_Available = pywbem.Uint16(0)
+ No_Additional_Information = pywbem.Uint16(1)
+ Stressed = pywbem.Uint16(2)
+ Predictive_Failure = pywbem.Uint16(3)
+ Non_Recoverable_Error = pywbem.Uint16(4)
+ Supporting_Entity_in_Error = pywbem.Uint16(5)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Not Available',
+ 1: 'No Additional Information',
+ 2: 'Stressed',
+ 3: 'Predictive Failure',
+ 4: 'Non-Recoverable Error',
+ 5: 'Supporting Entity in Error'
+ }
+
+ class Status(object):
+ OK = 'OK'
+ Error = 'Error'
+ Degraded = 'Degraded'
+ Unknown = 'Unknown'
+ Pred_Fail = 'Pred Fail'
+ Starting = 'Starting'
+ Stopping = 'Stopping'
+ Service = 'Service'
+ Stressed = 'Stressed'
+ NonRecover = 'NonRecover'
+ No_Contact = 'No Contact'
+ Lost_Comm = 'Lost Comm'
+ Stopped = 'Stopped'
+
+ class HealthState(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(5)
+ Degraded_Warning = pywbem.Uint16(10)
+ Minor_failure = pywbem.Uint16(15)
+ Major_failure = pywbem.Uint16(20)
+ Critical_failure = pywbem.Uint16(25)
+ Non_recoverable_error = pywbem.Uint16(30)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 5: 'OK',
+ 10: 'Degraded/Warning',
+ 15: 'Minor failure',
+ 20: 'Major failure',
+ 25: 'Critical failure',
+ 30: 'Non-recoverable error'
+ }
+
+ class JobState(object):
+ New = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Running = pywbem.Uint16(4)
+ Suspended = pywbem.Uint16(5)
+ Shutting_Down = pywbem.Uint16(6)
+ Completed = pywbem.Uint16(7)
+ Terminated = pywbem.Uint16(8)
+ Killed = pywbem.Uint16(9)
+ Exception = pywbem.Uint16(10)
+ Service = pywbem.Uint16(11)
+ Query_Pending = pywbem.Uint16(12)
+ # DMTF_Reserved = 13..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2: 'New',
+ 3: 'Starting',
+ 4: 'Running',
+ 5: 'Suspended',
+ 6: 'Shutting Down',
+ 7: 'Completed',
+ 8: 'Terminated',
+ 9: 'Killed',
+ 10: 'Exception',
+ 11: 'Service',
+ 12: 'Query Pending'
+ }
+
+ class GetError(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class KillJob(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Access_Denied = pywbem.Uint32(6)
+ Not_Found = pywbem.Uint32(7)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class RecoveryAction(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Do_Not_Continue = pywbem.Uint16(2)
+ Continue_With_Next_Job = pywbem.Uint16(3)
+ Re_run_Job = pywbem.Uint16(4)
+ Run_Recovery_Job = pywbem.Uint16(5)
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Do Not Continue',
+ 3: 'Continue With Next Job',
+ 4: 'Re-run Job',
+ 5: 'Run Recovery Job'
+ }
+
+ class RunDayOfWeek(object):
+ _Saturday = pywbem.Sint8(-7)
+ _Friday = pywbem.Sint8(-6)
+ _Thursday = pywbem.Sint8(-5)
+ _Wednesday = pywbem.Sint8(-4)
+ _Tuesday = pywbem.Sint8(-3)
+ _Monday = pywbem.Sint8(-2)
+ _Sunday = pywbem.Sint8(-1)
+ ExactDayOfMonth = pywbem.Sint8(0)
+ Sunday = pywbem.Sint8(1)
+ Monday = pywbem.Sint8(2)
+ Tuesday = pywbem.Sint8(3)
+ Wednesday = pywbem.Sint8(4)
+ Thursday = pywbem.Sint8(5)
+ Friday = pywbem.Sint8(6)
+ Saturday = pywbem.Sint8(7)
+ _reverse_map = {
+ 0: 'ExactDayOfMonth',
+ 1: 'Sunday',
+ 2: 'Monday',
+ 3: 'Tuesday',
+ 4: 'Wednesday',
+ 5: 'Thursday',
+ 6: 'Friday',
+ 7: 'Saturday',
+ -1: '-Sunday',
+ -7: '-Saturday',
+ -6: '-Friday',
+ -5: '-Thursday',
+ -4: '-Wednesday',
+ -3: '-Tuesday',
+ -2: '-Monday'
+ }
+
+ class RunMonth(object):
+ January = pywbem.Uint8(0)
+ February = pywbem.Uint8(1)
+ March = pywbem.Uint8(2)
+ April = pywbem.Uint8(3)
+ May = pywbem.Uint8(4)
+ June = pywbem.Uint8(5)
+ July = pywbem.Uint8(6)
+ August = pywbem.Uint8(7)
+ September = pywbem.Uint8(8)
+ October = pywbem.Uint8(9)
+ November = pywbem.Uint8(10)
+ December = pywbem.Uint8(11)
+ _reverse_map = {
+ 0: 'January',
+ 1: 'February',
+ 2: 'March',
+ 3: 'April',
+ 4: 'May',
+ 5: 'June',
+ 6: 'July',
+ 7: 'August',
+ 8: 'September',
+ 9: 'October',
+ 10: 'November',
+ 11: 'December'
+ }
+
+ class GetErrors(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class CommunicationStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Communication_OK = pywbem.Uint16(2)
+ Lost_Communication = pywbem.Uint16(3)
+ No_Contact = pywbem.Uint16(4)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Communication OK',
+ 3: 'Lost Communication',
+ 4: 'No Contact'
+ }
+
+ class OperationalStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ OK = pywbem.Uint16(2)
+ Degraded = pywbem.Uint16(3)
+ Stressed = pywbem.Uint16(4)
+ Predictive_Failure = pywbem.Uint16(5)
+ Error = pywbem.Uint16(6)
+ Non_Recoverable_Error = pywbem.Uint16(7)
+ Starting = pywbem.Uint16(8)
+ Stopping = pywbem.Uint16(9)
+ Stopped = pywbem.Uint16(10)
+ In_Service = pywbem.Uint16(11)
+ No_Contact = pywbem.Uint16(12)
+ Lost_Communication = pywbem.Uint16(13)
+ Aborted = pywbem.Uint16(14)
+ Dormant = pywbem.Uint16(15)
+ Supporting_Entity_in_Error = pywbem.Uint16(16)
+ Completed = pywbem.Uint16(17)
+ Power_Mode = pywbem.Uint16(18)
+ Relocating = pywbem.Uint16(19)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'OK',
+ 3: 'Degraded',
+ 4: 'Stressed',
+ 5: 'Predictive Failure',
+ 6: 'Error',
+ 7: 'Non-Recoverable Error',
+ 8: 'Starting',
+ 9: 'Stopping',
+ 10: 'Stopped',
+ 11: 'In Service',
+ 12: 'No Contact',
+ 13: 'Lost Communication',
+ 14: 'Aborted',
+ 15: 'Dormant',
+ 16: 'Supporting Entity in Error',
+ 17: 'Completed',
+ 18: 'Power Mode',
+ 19: 'Relocating'
+ }
+
+ class OperatingStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Servicing = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Stopping = pywbem.Uint16(4)
+ Stopped = pywbem.Uint16(5)
+ Aborted = pywbem.Uint16(6)
+ Dormant = pywbem.Uint16(7)
+ Completed = pywbem.Uint16(8)
+ Migrating = pywbem.Uint16(9)
+ Emigrating = pywbem.Uint16(10)
+ Immigrating = pywbem.Uint16(11)
+ Snapshotting = pywbem.Uint16(12)
+ Shutting_Down = pywbem.Uint16(13)
+ In_Test = pywbem.Uint16(14)
+ Transitioning = pywbem.Uint16(15)
+ In_Service = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Servicing',
+ 3: 'Starting',
+ 4: 'Stopping',
+ 5: 'Stopped',
+ 6: 'Aborted',
+ 7: 'Dormant',
+ 8: 'Completed',
+ 9: 'Migrating',
+ 10: 'Emigrating',
+ 11: 'Immigrating',
+ 12: 'Snapshotting',
+ 13: 'Shutting Down',
+ 14: 'In Test',
+ 15: 'Transitioning',
+ 16: 'In Service'
+ }
+
+ class LocalOrUtcTime(object):
+ Local_Time = pywbem.Uint16(1)
+ UTC_Time = pywbem.Uint16(2)
+ _reverse_map = {
+ 1: 'Local Time',
+ 2: 'UTC Time'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_Unspecified_Error = pywbem.Uint32(2)
+ Can_NOT_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Transition_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Start = pywbem.Uint16(2)
+ Suspend = pywbem.Uint16(3)
+ Terminate = pywbem.Uint16(4)
+ Kill = pywbem.Uint16(5)
+ Service = pywbem.Uint16(6)
+ # DMTF_Reserved = 7..32767
+ # Vendor_Reserved = 32768..65535
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'OK',
+ 2: 'Degraded',
+ 3: 'Error'
+ }
+
+@cmpi_logging.trace_function
+def _fill_nonkeys(job, model):
+ """
+ Fills into the model of instance all non-key properties.
+ """
+ model['Caption'] = 'Software installation job with id=%d' % job.jobid
+ model['CommunicationStatus'] = Values.CommunicationStatus.Not_Available
+ model['DeleteOnCompletion'] = job.delete_on_completion
+ try:
+ description = JOB_DESCRIPTIONS[job.__class__]
+ kwargs = job.job_kwargs
+ kwargs['jobid'] = job.jobid
+ model['Description'] = description % kwargs
+ except KeyError:
+ cmpi_logging.logger.error(
+ 'no description string found for job class %s' %
+ job.__class__.__name__)
+ model['Description'] = pywbem.CIMProperty('Description',
+ type='string', value=None)
+ if job.started:
+ if job.finished:
+ elapsed = job.finished - job.started
+ else:
+ elapsed = time.time() - job.started
+ model['ElapsedTime'] = pywbem.CIMDateTime(timedelta(seconds=elapsed))
+ else:
+ model["ElapsedTime"] = pywbem.CIMProperty('ElapsedTime',
+ type='datetime', value=None)
+ model['ErrorCode'] = pywbem.Uint16(0 if job.state != job.EXCEPTION else 1)
+ try:
+ model['JobState'], model['OperationalStatus'], model['JobStatus'] = {
+ jobs.YumJob.NEW : (Values.JobState.New,
+ [Values.OperationalStatus.Dormant], 'Enqueued'),
+ jobs.YumJob.RUNNING : (Values.JobState.Running,
+ [Values.OperationalStatus.OK], 'Running'),
+ jobs.YumJob.TERMINATED : (Values.JobState.Terminated,
+ [Values.OperationalStatus.Stopped], 'Terminated'),
+ jobs.YumJob.EXCEPTION : (Values.JobState.Exception
+ , [Values.OperationalStatus.Error]
+ , 'Failed'),
+ jobs.YumJob.COMPLETED : (Values.JobState.Completed
+ , [ Values.OperationalStatus.OK
+ , Values.OperationalStatus.Completed]
+ , 'Finished successfully')
+ }[job.state]
+ except KeyError:
+ cmpi_logging.logger.error('unknown job state: %s' % job.state)
+ model['JobState'] = pywbem.CIMProperty('JobState',
+ type='uint16', value=None)
+ model['OperationalStatus'] = [Values.OperationalStatus.Unknown]
+ model['JobStatus'] = 'Unknown'
+ if 'method_name' in job.metadata:
+ model['MethodName'] = job.metadata['method_name']
+ else:
+ model["MethodName"] = pywbem.CIMProperty('MethodName',
+ type='string', value=None)
+ model['Name'] = job.metadata['name']
+ model['LocalOrUtcTime'] = Values.LocalOrUtcTime.UTC_Time
+ model['PercentComplete'] = pywbem.Uint16(
+ 100 if job.state == job.COMPLETED else (
+ 50 if job.state == job.RUNNING else
+ 0))
+ model['Priority'] = pywbem.Uint32(job.priority)
+ if job.started:
+ model['StartTime'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.started))
+ model['TimeBeforeRemoval'] = pywbem.CIMDateTime(timedelta(
+ seconds=job.time_before_removal))
+ model['TimeOfLastStateChange'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.last_change))
+ model['TimeSubmitted'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.created))
+
+@cmpi_logging.trace_function
+def job2model(job, keys_only=True, model=None):
+ """
+ @param job can either be jobs.YumAsyncJob or integer
+ @param model if not None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ if not isinstance(job, (int, long, jobs.YumAsyncJob)):
+ raise TypeError("job must be an instance of YumAsyncJob")
+ if isinstance(job, jobs.YumAsyncJob) and not job.async:
+ raise ValueError("job must be asynchronous")
+ if not keys_only and isinstance(job, (int, long)):
+ raise TypeError("job must be an instance of YumAsyncJob"
+ " filling non-key properties")
+
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareInstallationJob",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareInstallationJob",
+ path=model)
+
+ jobid = job.jobid if isinstance(job, jobs.YumAsyncJob) else job
+ model['InstanceID'] = 'LMI:SoftwareInstallationJob:%d' % jobid
+ if isinstance(model, pywbem.CIMInstance):
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ if not keys_only:
+ _fill_nonkeys(job, model)
+ return model
+
+@cmpi_logging.trace_function
+def object_path2job(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if (not "InstanceID" in op or not op['InstanceID']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, "Wrong keys.")
+ instid = op['InstanceID']
+ if not instid.lower().startswith("lmi:softwareinstallationjob:"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "InstanceID must start with LMI:SoftwareInstallationJob: prefix.")
+ try:
+ instid = int(instid[len("LMI:SoftwareInstallationJob:"):])
+ except ValueError:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Invalid InstanceID "%s"' % instid)
+ try:
+ return YumDB.get_instance().get_job(instid)
+ except errors.JobNotFound:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No such job "%s".' % op['InstanceID'])
+
+@cmpi_logging.trace_function
+def modify_instance(instance):
+ """
+ This call modifies the job's parameters according to given instance.
+ """
+ job = object_path2job(instance.path)
+ ydb = YumDB.get_instance()
+ update_kwargs = {}
+ reschedule_kwargs = {}
+ # all modifiable properties
+ prop_name_map = {
+ "name" : "name",
+ "priority" : "priority",
+ "deleteoncompletion" : "delete_on_completion",
+ "timebeforeremoval" : "time_before_removal"
+ }
+ metadata_props = {"name"}
+ reschedule_props = {"delete_on_completion", "time_before_removal"}
+ for name, prop in instance.properties.items():
+ if prop is None:
+ cmpi_logging.logger.warn('property "%s" is None')
+ continue
+ name = name.lower()
+ try:
+ pname = prop_name_map[name]
+ if pname == "priority" and job.priority != prop.value:
+ cmpi_logging.logger.info(
+ 'changing priority of job %s to %d', job, prop.value)
+ job = ydb.set_job_priority(job.jobid, prop.value)
+ elif pname in reschedule_props:
+ if getattr(job, pname) == prop.value:
+ continue
+ if pname == "time_before_removal":
+ value = prop.value.timedelta.total_seconds()
+ else:
+ value = prop.value
+ reschedule_kwargs[pname] = value
+ else:
+ if pname in metadata_props:
+ if not 'metadata' in update_kwargs:
+ update_kwargs['metadata'] = {}
+ update_kwargs['metadata'][pname] = prop.value
+ else:
+ update_kwargs[pname] = prop.value
+ except KeyError:
+ if name == 'instanceid':
+ continue
+ cmpi_logging.logger.warn("skipping property %s: %s", name, prop)
+
+ if reschedule_kwargs:
+ for prop in ('delete_on_completion', 'time_before_removal'):
+ if prop not in reschedule_kwargs:
+ reschedule_kwargs[prop] = getattr(job, prop)
+ cmpi_logging.logger.info('rescheduling job %s to: %s',
+ job, ", ".join("%s=%s"%(k, v) for k, v in
+ reschedule_kwargs.items()))
+ job = ydb.reschedule_job(job.jobid, **reschedule_kwargs)
+
+ if update_kwargs:
+ cmpi_logging.logger.info('changing atributes of job %s to: %s',
+ job, ", ".join("%s=%s"%(k, v) for k, v in
+ update_kwargs.items()))
+ job = ydb.update_job(job.jobid, **update_kwargs)
+
+ return job2model(job, keys_only=False, model=instance)
+
+@cmpi_logging.trace_function
+def job2error(job):
+ """
+ @return instance of CIM_Error if job is in EXCEPTION state,
+ None otherwise
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be isntance of YumJob")
+ if job.state == job.EXCEPTION:
+ errortup = job.result_data
+ kwargs = {}
+ if issubclass(errortup[0],
+ (errors.RepositoryNotFound, errors.PackageNotFound)):
+ kwargs['status_code'] = Error.Values. \
+ CIMStatusCode.CIM_ERR_NOT_FOUND
+ if issubclass(errortup[0], errors.PackageNotFound):
+ kwargs['status_code_description'] = \
+ "Package not found"
+ else:
+ kwargs['status_code_description'] = \
+ "Repository not found"
+ elif issubclass(errortup[0], errors.PackageAlreadyInstalled):
+ kwargs['status_code'] = Error.Values. \
+ CIMStatusCode.CIM_ERR_ALREADY_EXISTS
+ kwargs['message'] = getattr(errortup[1], 'message',
+ str(errortup[1]))
+ value = Error.make_instance(**kwargs)
+ return value
diff --git a/src/software/openlmi/software/core/InstallationService.py b/src/software/openlmi/software/core/InstallationService.py
new file mode 100644
index 0000000..2935849
--- /dev/null
+++ b/src/software/openlmi/software/core/InstallationService.py
@@ -0,0 +1,729 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to LMI_SoftwareInstallationService
+provider.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+from openlmi.software.core import ComputerSystem
+from openlmi.software.core import Identity
+from openlmi.software.core import SystemCollection
+from openlmi.software.yumdb import errors
+from openlmi.software.yumdb import YumDB
+
+class InstallationError(Exception):
+ """This exception shall be raised upon any error within
+ install_or_remove_package() function.
+ """
+ def __init__(self, return_code, description):
+ Exception.__init__(self, return_code, description)
+ @property
+ def return_code(self):
+ """@return return code of CIM method"""
+ return self.args[0]
+ @property
+ def description(self):
+ """@return description of error"""
+ return self.args[1]
+
+class Values(object):
+ class DetailedStatus(object):
+ Not_Available = pywbem.Uint16(0)
+ No_Additional_Information = pywbem.Uint16(1)
+ Stressed = pywbem.Uint16(2)
+ Predictive_Failure = pywbem.Uint16(3)
+ Non_Recoverable_Error = pywbem.Uint16(4)
+ Supporting_Entity_in_Error = pywbem.Uint16(5)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Not Available',
+ 1: 'No Additional Information',
+ 2: 'Stressed',
+ 3: 'Predictive Failure',
+ 4: 'Non-Recoverable Error',
+ 5: 'Supporting Entity in Error'
+ }
+
+ class RequestedState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 5: 'No Change',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Deferred',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset',
+ 12: 'Not Applicable'
+ }
+
+ class HealthState(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(5)
+ Degraded_Warning = pywbem.Uint16(10)
+ Minor_failure = pywbem.Uint16(15)
+ Major_failure = pywbem.Uint16(20)
+ Critical_failure = pywbem.Uint16(25)
+ Non_recoverable_error = pywbem.Uint16(30)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 5: 'OK',
+ 10: 'Degraded/Warning',
+ 15: 'Minor failure',
+ 20: 'Major failure',
+ 25: 'Critical failure',
+ 30: 'Non-recoverable error'
+ }
+
+ class InstallFromURI(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ URI_not_accessible = pywbem.Uint32(4107)
+ # Method_Reserved = 4108..32767
+ # Vendor_Specific = 32768..65535
+ class InstallOptions(object):
+ Defer_target_system_reset = pywbem.Uint16(2)
+ Force_installation = pywbem.Uint16(3)
+ Install = pywbem.Uint16(4)
+ Update = pywbem.Uint16(5)
+ Repair = pywbem.Uint16(6)
+ Reboot = pywbem.Uint16(7)
+ Password = pywbem.Uint16(8)
+ Uninstall = pywbem.Uint16(9)
+ Log = pywbem.Uint16(10)
+ SilentMode = pywbem.Uint16(11)
+ AdministrativeMode = pywbem.Uint16(12)
+ ScheduleInstallAt = pywbem.Uint16(13)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 2 : "Defer Target/System Reset",
+ 3 : "Force Installation",
+ 4 : "Install",
+ 5 : "Update",
+ 6 : "Repair",
+ 7 : "Reboot",
+ 8 : "Password",
+ 9 : "Uninstall",
+ 10 : "Log",
+ 11 : "SilentMode",
+ 12 : "AdministrativeMode",
+ 13 : "ScheduleInstallAt",
+ }
+
+ InstallOptions.supported = {
+ InstallOptions.Install,
+ InstallOptions.Update,
+ InstallOptions.Uninstall,
+ InstallOptions.Force_installation,
+ InstallOptions.Repair,
+ }
+
+
+ class CheckSoftwareIdentity(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Reserved = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ Cannot_add_to_Collection = pywbem.Uint32(4108)
+ Asynchronous_Job_already_in_progress = pywbem.Uint32(4109)
+ # Method_Reserved = 4110..32767
+ # Vendor_Specific = 32768..65535
+ class InstallCharacteristics(object):
+ Target_automatic_reset = pywbem.Uint16(2)
+ System_automatic_reset = pywbem.Uint16(3)
+ Separate_target_reset_Required = pywbem.Uint16(4)
+ Separate_system_reset_Required = pywbem.Uint16(5)
+ Manual_Reboot_Required = pywbem.Uint16(6)
+ No_Reboot_Required = pywbem.Uint16(7)
+ User_Intervention_recommended = pywbem.Uint16(8)
+ MAY_be_added_to_specified_Collection = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 0x7FFF..0xFFFF
+
+ class ChangeAffectedElementsAssignedSequence(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Error_Occured = pywbem.Uint32(2)
+ Busy = pywbem.Uint32(3)
+ Invalid_Reference = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = 7..32767
+ # Vendor_Specified = 32768..65535
+
+ class TransitioningToState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0: 'Unknown',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 5: 'No Change',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Defer',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset',
+ 12: 'Not Applicable'
+ }
+
+ class EnabledDefault(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ No_Default = pywbem.Uint16(7)
+ Quiesce = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 5: 'Not Applicable',
+ 6: 'Enabled but Offline',
+ 7: 'No Default',
+ 9: 'Quiesce'
+ }
+
+ class EnabledState(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shutting_Down = pywbem.Uint16(4)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ In_Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Starting = pywbem.Uint16(10)
+ # DMTF_Reserved = 11..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shutting Down',
+ 5: 'Not Applicable',
+ 6: 'Enabled but Offline',
+ 7: 'In Test',
+ 8: 'Deferred',
+ 9: 'Quiesce',
+ 10: 'Starting'
+ }
+
+ class AvailableRequestedStates(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Defer',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset'
+ }
+
+ class Status(object):
+ OK = 'OK'
+ Error = 'Error'
+ Degraded = 'Degraded'
+ Unknown = 'Unknown'
+ Pred_Fail = 'Pred Fail'
+ Starting = 'Starting'
+ Stopping = 'Stopping'
+ Service = 'Service'
+ Stressed = 'Stressed'
+ NonRecover = 'NonRecover'
+ No_Contact = 'No Contact'
+ Lost_Comm = 'Lost Comm'
+ Stopped = 'Stopped'
+
+ class CommunicationStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Communication_OK = pywbem.Uint16(2)
+ Lost_Communication = pywbem.Uint16(3)
+ No_Contact = pywbem.Uint16(4)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Communication OK',
+ 3: 'Lost Communication',
+ 4: 'No Contact'
+ }
+
+ class OperationalStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ OK = pywbem.Uint16(2)
+ Degraded = pywbem.Uint16(3)
+ Stressed = pywbem.Uint16(4)
+ Predictive_Failure = pywbem.Uint16(5)
+ Error = pywbem.Uint16(6)
+ Non_Recoverable_Error = pywbem.Uint16(7)
+ Starting = pywbem.Uint16(8)
+ Stopping = pywbem.Uint16(9)
+ Stopped = pywbem.Uint16(10)
+ In_Service = pywbem.Uint16(11)
+ No_Contact = pywbem.Uint16(12)
+ Lost_Communication = pywbem.Uint16(13)
+ Aborted = pywbem.Uint16(14)
+ Dormant = pywbem.Uint16(15)
+ Supporting_Entity_in_Error = pywbem.Uint16(16)
+ Completed = pywbem.Uint16(17)
+ Power_Mode = pywbem.Uint16(18)
+ Relocating = pywbem.Uint16(19)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'OK',
+ 3: 'Degraded',
+ 4: 'Stressed',
+ 5: 'Predictive Failure',
+ 6: 'Error',
+ 7: 'Non-Recoverable Error',
+ 8: 'Starting',
+ 9: 'Stopping',
+ 10: 'Stopped',
+ 11: 'In Service',
+ 12: 'No Contact',
+ 13: 'Lost Communication',
+ 14: 'Aborted',
+ 15: 'Dormant',
+ 16: 'Supporting Entity in Error',
+ 17: 'Completed',
+ 18: 'Power Mode',
+ 19: 'Relocating'
+ }
+
+ class OperatingStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Servicing = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Stopping = pywbem.Uint16(4)
+ Stopped = pywbem.Uint16(5)
+ Aborted = pywbem.Uint16(6)
+ Dormant = pywbem.Uint16(7)
+ Completed = pywbem.Uint16(8)
+ Migrating = pywbem.Uint16(9)
+ Emigrating = pywbem.Uint16(10)
+ Immigrating = pywbem.Uint16(11)
+ Snapshotting = pywbem.Uint16(12)
+ Shutting_Down = pywbem.Uint16(13)
+ In_Test = pywbem.Uint16(14)
+ Transitioning = pywbem.Uint16(15)
+ In_Service = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Servicing',
+ 3: 'Starting',
+ 4: 'Stopping',
+ 5: 'Stopped',
+ 6: 'Aborted',
+ 7: 'Dormant',
+ 8: 'Completed',
+ 9: 'Migrating',
+ 10: 'Emigrating',
+ 11: 'Immigrating',
+ 12: 'Snapshotting',
+ 13: 'Shutting Down',
+ 14: 'In Test',
+ 15: 'Transitioning',
+ 16: 'In Service'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_or_Unspecified_Error = pywbem.Uint32(2)
+ Cannot_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+
+ class InstallFromByteStream(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ # Method_Reserved = 4108..32767
+ # Vendor_Specific = 32768..65535
+
+ InstallFromByteStream.InstallOptions = InstallFromURI.InstallOptions
+
+ class InstallFromSoftwareIdentity(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ Cannot_add_to_Collection = pywbem.Uint32(4108)
+ # Method_Reserved = 4109..32767
+ # Vendor_Specific = 32768..65535
+
+ InstallFromSoftwareIdentity.InstallOptions = InstallFromURI.InstallOptions
+
+ class StartMode(object):
+ Automatic = 'Automatic'
+ Manual = 'Manual'
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'OK',
+ 2: 'Degraded',
+ 3: 'Error'
+ }
+
+def get_path():
+ """@return instance name with prefilled properties"""
+ op = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareInstallationService",
+ namespace="root/cimv2")
+ op['CreationClassName'] = op.classname
+ systemop = ComputerSystem.get_path()
+ op["SystemCreationClassName"] = systemop.classname
+ op['SystemName'] = systemop["Name"]
+ op["Name"] = "LMI:SoftwareInstallationService"
+ return op
+
+@cmpi_logging.trace_function
+def check_path(env, service, prop_name):
+ """
+ Checks instance name of SoftwareInstallationService.
+ @param prop_name name of object name; used for error descriptions
+ """
+ if not isinstance(service, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_service = get_path()
+ ch = env.get_cimom_handle()
+ if service.namespace != our_service.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_service.namespace))
+ if not ch.is_subclass(our_service.namespace,
+ sub=service.classname,
+ super=our_service.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a sublass of %s" % (
+ prop_name, our_service.classname))
+ for key in our_service.keybindings.keys():
+ if not key in service:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" is missing %s key property" % ( prop_name, key))
+ if service[key] != our_service[key]:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" key property %s(%s) does not match \"%s\"" %(
+ prop_name, key, service[key], our_service[key]))
+ return True
+
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether prop_name property of op object path is correct.
+ If not, an exception will be raised.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
+
+@cmpi_logging.trace_function
+def _check_target_and_collection(env, src_type, target, collection):
+ """
+ Checks Target and Collection parameters of provider's installation
+ methods.
+ """
+ values = Values.InstallFromSoftwareIdentity
+ if target:
+ try:
+ ComputerSystem.check_path(env, target, "Target")
+ except pywbem.CIMError as exc:
+ raise InstallationError(values.Unspecified_Error,
+ "Target must be either NULL or match managed"
+ " computer system: %s", str(exc))
+ if collection:
+ try:
+ SystemCollection.check_path(env, collection, "Collection")
+ except pywbem.CIMError as exc:
+ raise InstallationError(values.Unspecified_Error,
+ "Collection does not match system software collection: %s" %
+ str(exc))
+ if target and collection:
+ raise InstallationError(values.Unspecified_Error,
+ "Only one of Target and Collection parameters can be specified"
+ " at the same time.")
+ if not target and not collection:
+ raise InstallationError(values.Unspecified_Error,
+ "Either Target or Collection parameter must be specified."
+ if src_type == "identity" else
+ "Missing Target parameter.")
+
+@cmpi_logging.trace_function
+def _install_or_remove_check_params(
+ env, src_type, source, target, collection,
+ install_options,
+ install_options_values):
+ """
+ Checks parameters of provider's installation methods.
+ Upon any invalid option an InstallationError will be raised.
+ @return tuple (action, force, repair)
+ where action is one of Values.InstallFromSoftwareIdentity properties
+ """
+ values = Values.InstallFromSoftwareIdentity
+ supported_options = values.InstallOptions.supported.copy()
+ if src_type == "uri":
+ supported_options.remove(values.InstallOptions.Uninstall)
+
+ if not src_type in {"uri", "identity"}:
+ raise ValueError('uri must be one of {"uri", "identity"}')
+ if not source:
+ raise InstallationError(values.Unspecified_Error,
+ "Missing %s parameter." % (
+ "URI" if src_type == "uri" else "Source"))
+ if not install_options:
+ install_options = []
+ elif not isinstance(install_options, list):
+ raise InstallationError(values.Unspecified_Error,
+ "InstallOptions must be a list of uint16 values.")
+ options = {p for p in install_options}
+
+ if options - supported_options:
+ raise InstallationError(values.Unspecified_Error,
+ "unsupported install options: {%s}" %
+ ", ".join([ values.InstallOptions._reverse_map[p]
+ for p in options - supported_options]))
+ if install_options_values and len(options) != len(install_options_values):
+ raise InstallationError(values.Unspecified_Error,
+ "InstallOptions array must have the same"
+ " length as InstallOptionsValues: %d != %d" % (
+ len(options), len(install_options_values)))
+ if install_options_values:
+ for opt, val in zip(install_options, install_options_values):
+ if val:
+ raise InstallationError(values.Unspecified_Error,
+ "install option \"%s\" can not have any"
+ " associated value: %s" % (opt, val))
+ _check_target_and_collection(env, src_type, target, collection)
+ exclusive = [opt for opt in options if opt in {
+ values.InstallOptions.Install,
+ values.InstallOptions.Update,
+ values.InstallOptions.Uninstall }]
+ if len(exclusive) > 1:
+ raise InstallationError(values.Unspecified_Error,
+ "specified more than one mutually exclusive option at once: {%s}" %
+ ", ".join([ values.InstallOptions._reverse_map[p]
+ for p in exclusive]))
+ if not exclusive:
+ exclusive.append(values.InstallOptions.Install)
+ return ( exclusive[0]
+ , values.InstallOptions.Force_installation in options
+ , values.InstallOptions.Repair in options)
+
+@cmpi_logging.trace_function
+def install_or_remove_package(env, src_type, source, target, collection,
+ install_options,
+ install_options_values):
+ """
+ @param src_type is one of {"uri", "identity"}
+ """
+ values = Values.InstallFromSoftwareIdentity
+ (action, force, repair) = _install_or_remove_check_params(
+ env, src_type, source, target, collection,
+ install_options, install_options_values)
+ try:
+ ydb = YumDB.get_instance()
+ if action == values.InstallOptions.Uninstall:
+ nevra = Identity.object_path2nevra(
+ source, with_epoch='ALWAYS')
+ cmpi_logging.logger.info('removing package %s', nevra)
+ jobid = ydb.remove_package(nevra, async=True)
+ else:
+ update = action == values.InstallOptions.Update
+ if src_type == "uri":
+ cmpi_logging.logger.info('%s package "%s"',
+ 'updating' if update else 'installing', source)
+ jobid = ydb.install_package_from_uri(
+ source, update_only=update, force=force or repair,
+ async=True)
+ else: # software identity
+ nevra = Identity.object_path2nevra(
+ source, with_epoch='ALWAYS')
+ if update:
+ jobid = ydb.update_package(nevra,
+ force=force or repair, async=True)
+ else:
+ jobid = ydb.install_package(
+ nevra, force=force or repair, async=True)
+ cmpi_logging.logger.info('installation job %s for pkg "%s"'
+ ' enqueued', jobid, nevra)
+ return jobid
+ except (pywbem.CIMError, errors.InvalidURI) as exc:
+ cmpi_logging.logger.error('failed to install/remove package "%s"'
+ ' from %s: %s', source, src_type, str(exc))
+ raise InstallationError(values.Unspecified_Error, str(exc))
+
diff --git a/src/software/openlmi/software/core/MethodResult.py b/src/software/openlmi/software/core/MethodResult.py
new file mode 100644
index 0000000..7bfdac5
--- /dev/null
+++ b/src/software/openlmi/software/core/MethodResult.py
@@ -0,0 +1,84 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Just a common functionality related to class LMI_SoftwareMethodResult.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+from openlmi.software.yumdb import jobs, errors, YumDB
+
+@cmpi_logging.trace_function
+def object_path2jobid(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be a CIMInstanceName")
+ if not op["InstanceID"].lower().startswith('lmi:softwaremethodresult:'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing 'LMI:SoftwareMethodResult:' prefix in InstanceID.")
+ instid = op['InstanceID'][len('LMI:SoftwareMethodResult:'):]
+ try:
+ instid = int(instid)
+ except ValueError:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Failed to parse InstanceID.")
+ return instid
+
+@cmpi_logging.trace_function
+def object_path2job(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ instid = object_path2jobid(op)
+ try:
+ return YumDB.get_instance().get_job(instid)
+ except errors.JobNotFound:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "No such job.")
+
+@cmpi_logging.trace_function
+def job2model(job, keys_only=True, model=None):
+ """
+ This accepts job's id and produces corresponding result model.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be an instance of YumJob")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareMethodResult",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareMethodResult", path=model)
+ model['InstanceID'] = "LMI:SoftwareMethodResult:"+str(job.jobid)
+ if not keys_only:
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ model['Caption'] = 'Result of method %s' % job.metadata['method_name']
+ model['Description'] = (
+ 'Result of asynchronous job number %d created upon invocation'
+ " of %s's %s method." % (job.jobid,
+ "LMI_SoftwareInstallationService",
+ job.metadata['method_name']))
+ model['ElementName'] = 'MethodResult-%d' % job.jobid
+ #model['PostCallIndication'] = \ # TODO
+ #pywbem.CIMInstance(classname='CIM_InstMethodCall', ...)
+ #model['PreCallIndication'] = \ # TODO
+ #pywbem.CIMInstance(classname='CIM_InstMethodCall', ...)
+ return model
+
diff --git a/src/software/openlmi/software/core/SoftwareFileCheck.py b/src/software/openlmi/software/core/SoftwareFileCheck.py
deleted file mode 100644
index f63e510..0000000
--- a/src/software/openlmi/software/core/SoftwareFileCheck.py
+++ /dev/null
@@ -1,515 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""
-Just a common functionality related to SoftwareFileCheck provider.
-"""
-
-import collections
-import hashlib
-import os
-import pywbem
-import stat
-import yum
-
-from openlmi.common import cmpi_logging
-from openlmi.software import util
-from openlmi.software.yumdb import YumDB
-from openlmi.software.yumdb import packageinfo
-from openlmi.software.yumdb import packagecheck
-
-PASSED_FLAGS_DESCRIPTIONS = (
- "Existence",
- "File Type",
- "File Size",
- "File Mode",
- "File Checksum",
- "Device major/minor number",
- "Symlink Target",
- "User Ownership", "Group Ownership",
- "Modify Time")
-
-# Named tuple to store results of rpm file check as pywbem values, all results
-# are in the form:
-# (expected, reality)
-# where
-# expected is value from rpm package
-# reality is value obtained from installed file
-# None means, that value could not be obtained. Except for "exists" and
-# "md5_checksum" attributes, where "exists" is boolean and "md5_checksum" is
-# a string.
-# for example:
-# file_check.file_type == (4, 3)
-FileCheck = collections.namedtuple('FileCheck', #pylint: disable=C0103
- 'exists, md5_checksum, file_type, file_size, file_mode, '
- 'file_checksum, device, link_target, user_id, group_id, '
- 'last_modification_time')
-
-@cmpi_logging.trace_function
-def checksumtype_num2hash(csumt):
- """
- @param csumt checksum type as a number obtained from package
- @return hash function object corresponding to csumt
- """
- return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
-
-@cmpi_logging.trace_function
-def checksumtype_str2pywbem(alg):
- """
- @param alg is a name of algorithm used for checksum
- @return pywbem number corresponding to given alg
- """
- try:
- res = packagecheck.CHECKSUMTYPE_STR2NUM[alg.lower()]
- except KeyError:
- res = 0
- return pywbem.Uint16(res)
-
-@cmpi_logging.trace_function
-def filetype_str2pywbem(file_type):
- """
- @param file_type is a name of file type obtained from pkg headers
- @return pywbem number corresponding to thus file type
- """
- try:
- return pywbem.Uint16(
- { 'file' : Values.FileType.File
- , 'directory' : Values.FileType.Directory
- , 'symlink' : Values.FileType.Symlink
- , 'fifo' : Values.FileType.FIFO
- , 'character device' : Values.FileType.Character_Device
- , 'block device' : Values.FileType.Block_Device
- }[file_type])
- except KeyError:
- return Values.FileType.Unknown
-
-@cmpi_logging.trace_function
-def filetype_mode2pywbem(mode):
- """
- @param mode is a raw file mode as integer
- @return pywbem numeric value of file's type
- """
- for i, name in enumerate(
- ('REG', 'DIR', 'LNK', 'FIFO', 'CHR', 'BLK'), 1):
- if getattr(stat, 'S_IS' + name)(mode):
- return pywbem.Uint16(i)
- return pywbem.Uint16(0)
-
-@cmpi_logging.trace_function
-def mode2pywbem_flags(mode):
- """
- @param mode if None, file does not exist
- @return list of integer flags describing file's access permissions
- """
- if mode is None:
- return None
- flags = []
- for i, flag in enumerate((
- stat.S_IXOTH,
- stat.S_IWOTH,
- stat.S_IROTH,
- stat.S_IXGRP,
- stat.S_IWGRP,
- stat.S_IRGRP,
- stat.S_IXUSR,
- stat.S_IWUSR,
- stat.S_IRUSR,
- stat.S_ISVTX,
- stat.S_ISGID,
- stat.S_ISUID)):
- if flag & mode:
- flags.append(pywbem.Uint8(i))
- return flags
-
-@cmpi_logging.trace_function
-def hashfile(afile, hashers, blocksize=65536):
- """
- @param hashers is a list of hash objects
- @return list of digest strings (in hex format) for each hash object
- given in the same order
- """
- if not isinstance(hashers, (tuple, list, set, frozenset)):
- hashers = (hashers, )
- buf = afile.read(blocksize)
- while len(buf) > 0:
- for hashfunc in hashers:
- hashfunc.update(buf)
- buf = afile.read(blocksize)
- return [ hashfunc.hexdigest() for hashfunc in hashers ]
-
-@cmpi_logging.trace_function
-def compute_checksums(checksum_type, file_type, file_path):
- """
- @param file_type is not a file, then zeroes are returned
- @param checksum_type selected hash algorithm to compute second
- checksum
- @return (md5sum, checksum)
- both checksums are computed from file_path's content
- first one is always md5, the second one depends on checksum_type
- if file does not exists, (None, None) is returned
- """
- hashers = [hashlib.md5()] #pylint: disable=E1101
- if checksum_type != packagecheck.CHECKSUMTYPE_STR2NUM["md5"]:
- hashers.append(checksumtype_num2hash(checksum_type)())
- if file_type != filetype_str2pywbem('file'):
- rslts = ['0'*len(h.hexdigest()) for h in hashers]
- else:
- try:
- with open(file_path, 'rb') as fobj:
- rslts = hashfile(fobj, hashers)
- except (OSError, IOError) as exc:
- cmpi_logging.logger.error("could not open file \"%s\""
- " for reading: %s", file_path, exc)
- return None, None
- return (rslts[0], rslts[1] if len(rslts) > 1 else rslts[0]*2)
-
-@cmpi_logging.trace_function
-def object_path2pkg_file(objpath):
- """
- @return (package_info, package_check)
- """
- if not isinstance(objpath, pywbem.CIMInstanceName):
- raise TypeError("objpath must be instance of CIMInstanceName, "
- "not \"%s\"" % objpath.__class__.__name__)
-
- if ( not objpath['Name'] or not objpath['SoftwareElementID']
- or not objpath['CheckID']
- or not objpath['CheckID'].endswith('#'+objpath['Name'])
- or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
- if objpath['SoftwareElementState'] not in ("2", 2):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Only \"Executable\" software element state supported")
- if not util.check_target_operating_system(objpath['TargetOperatingSystem']):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong target operating system.")
- if not objpath['Name'] or not objpath['Version']:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- 'Both "Name" and "Version" must be given')
- match = util.RE_NEVRA_OPT_EPOCH.match(objpath['SoftwareElementID'])
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong SotwareElementID. Expected valid nevra"
- " (name-epoch:version-release.arch).")
- if objpath['Version'] != match.group('ver'):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Version does not match version part in SoftwareElementID.")
-
- with YumDB.getInstance() as ydb:
- pkglist = ydb.filter_packages('installed', **util.nevra2filter(match))
- if len(pkglist) < 1:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "No matching package installed.")
- pkg = pkglist[0]
- pkg_check = ydb.check_package(pkg)
- return (pkg, pkg_check, pkg_check[objpath["Name"]])
-
-@cmpi_logging.trace_function
-def test_file(checksum_type, package_file):
- """
- @param checksum type is a pywbem value for ChecksumType property
- @return instance of FileCheck
- """
- if not isinstance(package_file, packagecheck.PackageFile):
- raise TypeError("package_file must be an instance of PackageFile"
- " not \"%s\"" % package_file.__class__.__name__)
- exists = os.path.lexists(package_file.path)
- md5_checksum = None
- expected = {
- "file_type" : filetype_str2pywbem(package_file.file_type),
- "user_id" : pywbem.Uint32(package_file.uid),
- "group_id" : pywbem.Uint32(package_file.gid),
- "file_mode" : pywbem.Uint32(package_file.mode),
- "file_size" : pywbem.Uint64(package_file.size),
- "link_target" : package_file.link_target,
- "file_checksum" : package_file.checksum,
- "device" : pywbem.Uint64(package_file.device)
- if package_file.device is not None else None,
- "last_modification_time" : pywbem.Uint64(package_file.mtime)
- }
- if not exists:
- reality = collections.defaultdict(lambda: None)
- else:
- fstat = os.lstat(package_file.path)
- reality = {
- "file_type" : filetype_mode2pywbem(fstat.st_mode),
- "user_id" : pywbem.Uint32(fstat.st_uid),
- "group_id" : pywbem.Uint32(fstat.st_gid),
- "file_mode" : pywbem.Uint32(fstat.st_mode),
- "file_size" : pywbem.Uint64(fstat.st_size),
- "last_modification_time" : pywbem.Uint64(fstat.st_mtime)
- }
- reality["device"] = (
- pywbem.Uint64(fstat.st_dev)
- if reality['file_type'] == filetype_str2pywbem("device")
- else None)
- reality["link_target"] = (os.readlink(package_file.path)
- if os.path.islink(package_file.path) else None)
- md5_checksum, checksum = compute_checksums(
- checksum_type, reality["file_type"], package_file.path)
- reality["file_checksum"] = checksum
- kwargs = dict(exists=exists, md5_checksum=md5_checksum,
- **dict((k, (expected[k], reality[k])) for k in expected))
- return FileCheck(**kwargs)
-
-@cmpi_logging.trace_function
-def _filecheck2model_flags(file_check):
- """
- @param file_check is an instance of FileCheck
- @return pywbem value for PassedFlags property
- """
- if not isinstance(file_check, FileCheck):
- raise TypeError("file_check must be an instance of FileCheck")
- flags = []
- for k, value in file_check._asdict().items(): #pylint: disable=W0212
- if isinstance(value, tuple):
- if ( k in ("last_modification_time", "file_size")
- and file_check.file_type[0] != filetype_str2pywbem('file')):
- # last_modification_time check is valid only for
- # regular files
- flag = file_check.exists
- elif ( k == "file_mode"
- and file_check.file_type[0] == filetype_str2pywbem('symlink')):
- # do not check mode of symlinks
- flag = ( file_check.exists
- and file_check.file_type[0] == file_check.file_type[1])
- else:
- flag = file_check.exists and value[0] == value[1]
- flags.append(flag)
- elif isinstance(value, bool):
- flags.append(value)
- return flags
-
-@cmpi_logging.trace_function
-def filecheck_passed(file_check):
- """
- @return True if installed file passed all checks.
- """
- return all(_filecheck2model_flags(file_check))
-
-@cmpi_logging.trace_function
-def _fill_non_key_values(model, pkg_check, pkg_file, file_check=None):
- """
- Fills a non key values into instance of SoftwareFileCheck.
- """
- model['FileName'] = os.path.basename(pkg_file.path)
- model['FileChecksumType'] = csumt = pywbem.Uint16(
- pkg_check.file_checksum_type)
- if file_check is None:
- file_check = test_file(csumt, pkg_file)
- for mattr, fattr in (
- ('FileType', 'file_type'),
- ('FileUserID', 'user_id'),
- ('FileGroupID', 'group_id'),
- ('FileMode', 'file_mode'),
- ('LastModificationTime', 'last_modification_time'),
- ('FileSize', 'file_size'),
- ('LinkTarget', 'link_target'),
- ('FileChecksum', 'file_checksum')):
- exp, rea = getattr(file_check, fattr)
- if exp is not None:
- model['Expected' + mattr] = exp
- if rea is not None:
- model[mattr] = rea
- model['ExpectedFileModeFlags'] = mode2pywbem_flags(file_check.file_mode[0])
- if file_check.exists:
- model['FileModeFlags'] = mode2pywbem_flags(file_check.file_mode[1])
- model['FileExists'] = file_check.exists
- if file_check.md5_checksum is not None:
- model['MD5Checksum'] = file_check.md5_checksum
- model['PassedFlags'] = _filecheck2model_flags(file_check)
- model['PassedFlagsDescriptions'] = list(PASSED_FLAGS_DESCRIPTIONS)
-
-@cmpi_logging.trace_function
-def filecheck2model(package_info, package_check, file_name, keys_only=True,
- model=None, file_check=None):
- """
- @param package_file is an instance of yumdb.PackageFile
- @param file_name a absolute file path contained in package
- @param keys_only if True, then only key values will be filed
- @param model if given, then this instance will be modified and
- returned
- @param file_check if not given, it will be computed
- @return instance of LMI_SoftwareFileCheck class with all desired
- values filed
- """
- if not isinstance(package_info, packageinfo.PackageInfo):
- raise TypeError(
- "package_info must be an instance ofyumdb.PackageInfo")
- if not isinstance(package_check, packagecheck.PackageCheck):
- raise TypeError(
- "package_check must be an instance of yumdb.PackageFile")
- if not file_name in package_check:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "File \"%s\" not found among package files" % file_name)
- if model is None:
- model = pywbem.CIMInstanceName("LMI_SoftwareFileCheck",
- namespace="root/cimv2")
- if not keys_only:
- model = pywbem.CIMInstance("LMI_SoftwareFileCheck", path=model)
- package_file = package_check[file_name]
- model['Name'] = package_file.path
- model['SoftwareElementID'] = package_info.nevra
- model['SoftwareElementState'] = Values.SoftwareElementState.Executable
- model['TargetOperatingSystem'] = pywbem.Uint16(
- util.get_target_operating_system()[0])
- model['Version'] = package_info.version
- model['CheckID'] = '%s#%s' % (package_info.name, package_file.path)
- if not keys_only:
- if file_check is not None:
- if not isinstance(file_check, FileCheck):
- raise TypeError("file_check must be an instance of FileCheck")
- _fill_non_key_values(model, package_check, package_file, file_check)
- return model
-
-class Values(object):
- """
- Enumerations of LMI_SoftwareFileCheck class properties.
- """
- class TargetOperatingSystem(object):
- Unknown = pywbem.Uint16(0)
- Other = pywbem.Uint16(1)
- MACOS = pywbem.Uint16(2)
- ATTUNIX = pywbem.Uint16(3)
- DGUX = pywbem.Uint16(4)
- DECNT = pywbem.Uint16(5)
- Tru64_UNIX = pywbem.Uint16(6)
- OpenVMS = pywbem.Uint16(7)
- HPUX = pywbem.Uint16(8)
- AIX = pywbem.Uint16(9)
- MVS = pywbem.Uint16(10)
- OS400 = pywbem.Uint16(11)
- OS_2 = pywbem.Uint16(12)
- JavaVM = pywbem.Uint16(13)
- MSDOS = pywbem.Uint16(14)
- WIN3x = pywbem.Uint16(15)
- WIN95 = pywbem.Uint16(16)
- WIN98 = pywbem.Uint16(17)
- WINNT = pywbem.Uint16(18)
- WINCE = pywbem.Uint16(19)
- NCR3000 = pywbem.Uint16(20)
- NetWare = pywbem.Uint16(21)
- OSF = pywbem.Uint16(22)
- DC_OS = pywbem.Uint16(23)
- Reliant_UNIX = pywbem.Uint16(24)
- SCO_UnixWare = pywbem.Uint16(25)
- SCO_OpenServer = pywbem.Uint16(26)
- Sequent = pywbem.Uint16(27)
- IRIX = pywbem.Uint16(28)
- Solaris = pywbem.Uint16(29)
- SunOS = pywbem.Uint16(30)
- U6000 = pywbem.Uint16(31)
- ASERIES = pywbem.Uint16(32)
- HP_NonStop_OS = pywbem.Uint16(33)
- HP_NonStop_OSS = pywbem.Uint16(34)
- BS2000 = pywbem.Uint16(35)
- LINUX = pywbem.Uint16(36)
- Lynx = pywbem.Uint16(37)
- XENIX = pywbem.Uint16(38)
- VM = pywbem.Uint16(39)
- Interactive_UNIX = pywbem.Uint16(40)
- BSDUNIX = pywbem.Uint16(41)
- FreeBSD = pywbem.Uint16(42)
- NetBSD = pywbem.Uint16(43)
- GNU_Hurd = pywbem.Uint16(44)
- OS9 = pywbem.Uint16(45)
- MACH_Kernel = pywbem.Uint16(46)
- Inferno = pywbem.Uint16(47)
- QNX = pywbem.Uint16(48)
- EPOC = pywbem.Uint16(49)
- IxWorks = pywbem.Uint16(50)
- VxWorks = pywbem.Uint16(51)
- MiNT = pywbem.Uint16(52)
- BeOS = pywbem.Uint16(53)
- HP_MPE = pywbem.Uint16(54)
- NextStep = pywbem.Uint16(55)
- PalmPilot = pywbem.Uint16(56)
- Rhapsody = pywbem.Uint16(57)
- Windows_2000 = pywbem.Uint16(58)
- Dedicated = pywbem.Uint16(59)
- OS_390 = pywbem.Uint16(60)
- VSE = pywbem.Uint16(61)
- TPF = pywbem.Uint16(62)
- Windows__R__Me = pywbem.Uint16(63)
- Caldera_Open_UNIX = pywbem.Uint16(64)
- OpenBSD = pywbem.Uint16(65)
- Not_Applicable = pywbem.Uint16(66)
- Windows_XP = pywbem.Uint16(67)
- z_OS = pywbem.Uint16(68)
- Microsoft_Windows_Server_2003 = pywbem.Uint16(69)
- Microsoft_Windows_Server_2003_64_Bit = pywbem.Uint16(70)
- Windows_XP_64_Bit = pywbem.Uint16(71)
- Windows_XP_Embedded = pywbem.Uint16(72)
- Windows_Vista = pywbem.Uint16(73)
- Windows_Vista_64_Bit = pywbem.Uint16(74)
- Windows_Embedded_for_Point_of_Service = pywbem.Uint16(75)
- Microsoft_Windows_Server_2008 = pywbem.Uint16(76)
- Microsoft_Windows_Server_2008_64_Bit = pywbem.Uint16(77)
- FreeBSD_64_Bit = pywbem.Uint16(78)
- RedHat_Enterprise_Linux = pywbem.Uint16(79)
- RedHat_Enterprise_Linux_64_Bit = pywbem.Uint16(80)
- Solaris_64_Bit = pywbem.Uint16(81)
- SUSE = pywbem.Uint16(82)
- SUSE_64_Bit = pywbem.Uint16(83)
- SLES = pywbem.Uint16(84)
- SLES_64_Bit = pywbem.Uint16(85)
- Novell_OES = pywbem.Uint16(86)
- Novell_Linux_Desktop = pywbem.Uint16(87)
- Sun_Java_Desktop_System = pywbem.Uint16(88)
- Mandriva = pywbem.Uint16(89)
- Mandriva_64_Bit = pywbem.Uint16(90)
- TurboLinux = pywbem.Uint16(91)
- TurboLinux_64_Bit = pywbem.Uint16(92)
- Ubuntu = pywbem.Uint16(93)
- Ubuntu_64_Bit = pywbem.Uint16(94)
- Debian = pywbem.Uint16(95)
- Debian_64_Bit = pywbem.Uint16(96)
- Linux_2_4_x = pywbem.Uint16(97)
- Linux_2_4_x_64_Bit = pywbem.Uint16(98)
- Linux_2_6_x = pywbem.Uint16(99)
- Linux_2_6_x_64_Bit = pywbem.Uint16(100)
- Linux_64_Bit = pywbem.Uint16(101)
- Other_64_Bit = pywbem.Uint16(102)
- Microsoft_Windows_Server_2008_R2 = pywbem.Uint16(103)
- VMware_ESXi = pywbem.Uint16(104)
- Microsoft_Windows_7 = pywbem.Uint16(105)
- CentOS_32_bit = pywbem.Uint16(106)
- CentOS_64_bit = pywbem.Uint16(107)
- Oracle_Enterprise_Linux_32_bit = pywbem.Uint16(108)
- Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109)
- eComStation_32_bitx = pywbem.Uint16(110)
-
- class SoftwareElementState(object):
- Deployable = pywbem.Uint16(0)
- Installable = pywbem.Uint16(1)
- Executable = pywbem.Uint16(2)
- Running = pywbem.Uint16(3)
-
- class FileType(object):
- Unknown = pywbem.Uint16(0)
- File = pywbem.Uint16(1)
- Directory = pywbem.Uint16(2)
- Symlink = pywbem.Uint16(3)
- FIFO = pywbem.Uint16(4)
- Character_Device = pywbem.Uint16(5)
- Block_Device = pywbem.Uint16(6)
-
diff --git a/src/software/openlmi/software/core/SoftwareInstalledPackage.py b/src/software/openlmi/software/core/SoftwareInstalledPackage.py
deleted file mode 100644
index 43572a9..0000000
--- a/src/software/openlmi/software/core/SoftwareInstalledPackage.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""
-Just a common functionality related to LMI_SoftwarePackage provider.
-"""
-
-import pywbem
-
-class Values(object):
- class Update(object):
- Already_newest = pywbem.Uint16(0)
- Successful_installation = pywbem.Uint16(1)
- Failed = pywbem.Uint16(2)
-
- class CheckIntegrity(object):
- Pass = pywbem.Uint32(0)
- Not_passed = pywbem.Uint32(1)
- Error = pywbem.Uint32(2)
-
diff --git a/src/software/openlmi/software/core/SoftwarePackage.py b/src/software/openlmi/software/core/SoftwarePackage.py
deleted file mode 100644
index de652ac..0000000
--- a/src/software/openlmi/software/core/SoftwarePackage.py
+++ /dev/null
@@ -1,412 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""
-Just a common functionality related to LMI_SoftwarePackage provider.
-"""
-
-import pywbem
-
-from openlmi.common import cmpi_logging
-from openlmi.software import util
-from openlmi.software.yumdb import PackageInfo, YumDB
-
-@cmpi_logging.trace_function
-def object_path2pkg(objpath, kind='installed'):
- """
- @param objpath must contain precise information of package,
- otherwise a CIM_ERR_NOT_FOUND error is raised
- @param kind one of {'installed', 'all', 'available'}
- says, where to look for given package
- """
- if not isinstance(objpath, pywbem.CIMInstanceName):
- raise TypeError("objpath must be an instance of CIMInstanceName")
- if not isinstance(kind, basestring):
- raise TypeError("kind must be a string")
- if not kind in ('installed', 'all', 'available'):
- raise ValueError('unsupported package list "%s"' % kind)
-
- if ( not objpath['Name'] or not objpath['SoftwareElementID']
- or not objpath['SoftwareElementID'].startswith(objpath['Name'])
- or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
- if not util.check_target_operating_system(objpath['TargetOperatingSystem']):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong target operating system.")
- if not objpath['Name'] or not objpath['Version']:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- 'Both "Name" and "Version" must be given')
- match = util.RE_NEVRA_OPT_EPOCH.match(objpath['SoftwareElementID'])
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong SotwareElementID. Expected valid nevra"
- " (name-[epoch:]version-release.arch).")
- if objpath['Version'] != match.group('ver'):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Version does not match version part in SoftwareElementID.")
- pkglist = YumDB.getInstance().filter_packages(kind,
- allow_duplicates=kind != 'installed',
- **util.nevra2filter(match))
- if len(pkglist) > 0:
- return pkglist[0]
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "No matching package found.")
-
-@cmpi_logging.trace_function
-def object_path2pkg_search(objpath):
- """
- similar to object_path2pkg, but tries to find best suitable
- package matching keys
-
- If any matching package is already installed, it is returned.
- Otherwise available package with highest version is returned.
-
- @param objpath may be object of CIMInstance or CIMInstanceName and
- must contain at least \"Name\" or \"SoftwareElementID\"
- @return instance PackageInfo
- """
- if isinstance(objpath, pywbem.CIMInstance):
- def _get_key(k):
- """@return value of instance's key"""
- value = objpath.properties.get(k, None)
- if isinstance(value, pywbem.CIMProperty):
- return value.value
- if value is not None:
- return value
- cmpi_logging.logger.error(
- 'missing key "%s" in inst.props', k)
- return objpath.path[k] if k in objpath.path else None
- elif isinstance(objpath, pywbem.CIMInstanceName):
- _get_key = lambda k: objpath[k] if k in objpath else None
- else:
- raise TypeError("objpath must be either CIMInstance"
- "or CIMInstanceName")
-
- # parse and check arguments
- filters = {}
- if _get_key('SoftwareElementID'):
- match = util.RE_NEVRA_OPT_EPOCH.match(_get_key('SoftwareElementID'))
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
- "SoftwareElementID could not be parsed.")
- filters = util.nevra2filter(match)
- else:
- for k in ('name', 'epoch', 'version', 'release', 'arch'):
- ikey = k if k != 'arch' else "architecture"
- if _get_key(ikey):
- filters[k] = _get_key(ikey)
-
- if not filters:
- raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
- "Too few key values given (give at least a Name).")
- if not 'name' in filters:
- raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
- "Missing either Name or SoftwareElementID property.")
-
- pkglist = YumDB.getInstance().filter_packages('all',
- allow_duplicates=True, sort=True, **filters)
- if len(pkglist) == 0:
- cmpi_logging.logger.error(
- 'could not find any matching package in list: %s',
- [p.nevra for p in pkglist])
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "No matching package found.")
- for pkg in pkglist: # check, whether package is already installed
- if pkg.installed:
- return pkg
- cmpi_logging.logger.info(
- ( 'found multiple matching packages'
- if len(pkglist) > 1 else 'exact match found'))
- return pkglist[-1] # select highest version
-
-@cmpi_logging.trace_function
-def pkg2model(pkg, keys_only=True, model=None):
- """
- @param model if None, will be filled with data, otherwise
- a new instance of CIMInstance or CIMObjectPath is created
- """
- if not isinstance(pkg, PackageInfo):
- raise TypeError("pkg must be an instance of PackageInfo")
- if model is None:
- model = pywbem.CIMInstanceName('LMI_SoftwarePackage',
- namespace='root/cimv2')
- if not keys_only:
- model = pywbem.CIMInstance('LMI_SoftwarePackage', path=model)
- if isinstance(model, pywbem.CIMInstance):
- def _set_key(k, value):
- """Sets the value of key property of cim instance"""
- model[k] = value
- model.path[k] = value #pylint: disable=E1103
- else:
- _set_key = model.__setitem__
- _set_key('Name', pkg.name)
- _set_key('SoftwareElementID', pkg.nevra)
- _set_key('SoftwareElementState',
- Values.SoftwareElementState.Executable
- if pkg.installed
- else Values.SoftwareElementState.Installable)
- _set_key('TargetOperatingSystem',
- pywbem.Uint16(util.get_target_operating_system()[0]))
- _set_key('Version', pkg.version)
- if not keys_only:
- model['Caption'] = pkg.summary
- model['Description'] = pkg.description
- if pkg.installed:
- model['InstallDate'] = pywbem.CIMDateTime(pkg.install_time)
- if pkg.vendor:
- model['Manufacturer'] = pkg.vendor
- model['Release'] = pkg.release
- model['Epoch'] = pywbem.Uint16(pkg.epoch)
- model["Architecture"] = pkg.arch
- model['License'] = pkg.license
- model['Group'] = pkg.group
- model['Size'] = pywbem.Uint64(pkg.size)
- return model
-
-class Values(object):
- class DetailedStatus(object):
- Not_Available = pywbem.Uint16(0)
- No_Additional_Information = pywbem.Uint16(1)
- Stressed = pywbem.Uint16(2)
- Predictive_Failure = pywbem.Uint16(3)
- Non_Recoverable_Error = pywbem.Uint16(4)
- Supporting_Entity_in_Error = pywbem.Uint16(5)
- # DMTF_Reserved = ..
- # Vendor_Reserved = 0x8000..
-
- class Status(object):
- OK = 'OK'
- Error = 'Error'
- Degraded = 'Degraded'
- Unknown = 'Unknown'
- Pred_Fail = 'Pred Fail'
- Starting = 'Starting'
- Stopping = 'Stopping'
- Service = 'Service'
- Stressed = 'Stressed'
- NonRecover = 'NonRecover'
- No_Contact = 'No Contact'
- Lost_Comm = 'Lost Comm'
- Stopped = 'Stopped'
-
- class HealthState(object):
- Unknown = pywbem.Uint16(0)
- OK = pywbem.Uint16(5)
- Degraded_Warning = pywbem.Uint16(10)
- Minor_failure = pywbem.Uint16(15)
- Major_failure = pywbem.Uint16(20)
- Critical_failure = pywbem.Uint16(25)
- Non_recoverable_error = pywbem.Uint16(30)
- # DMTF_Reserved = ..
- # Vendor_Specific = 32768..65535
-
- class TargetOperatingSystem(object):
- Unknown = pywbem.Uint16(0)
- Other = pywbem.Uint16(1)
- MACOS = pywbem.Uint16(2)
- ATTUNIX = pywbem.Uint16(3)
- DGUX = pywbem.Uint16(4)
- DECNT = pywbem.Uint16(5)
- Tru64_UNIX = pywbem.Uint16(6)
- OpenVMS = pywbem.Uint16(7)
- HPUX = pywbem.Uint16(8)
- AIX = pywbem.Uint16(9)
- MVS = pywbem.Uint16(10)
- OS400 = pywbem.Uint16(11)
- OS_2 = pywbem.Uint16(12)
- JavaVM = pywbem.Uint16(13)
- MSDOS = pywbem.Uint16(14)
- WIN3x = pywbem.Uint16(15)
- WIN95 = pywbem.Uint16(16)
- WIN98 = pywbem.Uint16(17)
- WINNT = pywbem.Uint16(18)
- WINCE = pywbem.Uint16(19)
- NCR3000 = pywbem.Uint16(20)
- NetWare = pywbem.Uint16(21)
- OSF = pywbem.Uint16(22)
- DC_OS = pywbem.Uint16(23)
- Reliant_UNIX = pywbem.Uint16(24)
- SCO_UnixWare = pywbem.Uint16(25)
- SCO_OpenServer = pywbem.Uint16(26)
- Sequent = pywbem.Uint16(27)
- IRIX = pywbem.Uint16(28)
- Solaris = pywbem.Uint16(29)
- SunOS = pywbem.Uint16(30)
- U6000 = pywbem.Uint16(31)
- ASERIES = pywbem.Uint16(32)
- HP_NonStop_OS = pywbem.Uint16(33)
- HP_NonStop_OSS = pywbem.Uint16(34)
- BS2000 = pywbem.Uint16(35)
- LINUX = pywbem.Uint16(36)
- Lynx = pywbem.Uint16(37)
- XENIX = pywbem.Uint16(38)
- VM = pywbem.Uint16(39)
- Interactive_UNIX = pywbem.Uint16(40)
- BSDUNIX = pywbem.Uint16(41)
- FreeBSD = pywbem.Uint16(42)
- NetBSD = pywbem.Uint16(43)
- GNU_Hurd = pywbem.Uint16(44)
- OS9 = pywbem.Uint16(45)
- MACH_Kernel = pywbem.Uint16(46)
- Inferno = pywbem.Uint16(47)
- QNX = pywbem.Uint16(48)
- EPOC = pywbem.Uint16(49)
- IxWorks = pywbem.Uint16(50)
- VxWorks = pywbem.Uint16(51)
- MiNT = pywbem.Uint16(52)
- BeOS = pywbem.Uint16(53)
- HP_MPE = pywbem.Uint16(54)
- NextStep = pywbem.Uint16(55)
- PalmPilot = pywbem.Uint16(56)
- Rhapsody = pywbem.Uint16(57)
- Windows_2000 = pywbem.Uint16(58)
- Dedicated = pywbem.Uint16(59)
- OS_390 = pywbem.Uint16(60)
- VSE = pywbem.Uint16(61)
- TPF = pywbem.Uint16(62)
- Windows__R__Me = pywbem.Uint16(63)
- Caldera_Open_UNIX = pywbem.Uint16(64)
- OpenBSD = pywbem.Uint16(65)
- Not_Applicable = pywbem.Uint16(66)
- Windows_XP = pywbem.Uint16(67)
- z_OS = pywbem.Uint16(68)
- Microsoft_Windows_Server_2003 = pywbem.Uint16(69)
- Microsoft_Windows_Server_2003_64_Bit = pywbem.Uint16(70)
- Windows_XP_64_Bit = pywbem.Uint16(71)
- Windows_XP_Embedded = pywbem.Uint16(72)
- Windows_Vista = pywbem.Uint16(73)
- Windows_Vista_64_Bit = pywbem.Uint16(74)
- Windows_Embedded_for_Point_of_Service = pywbem.Uint16(75)
- Microsoft_Windows_Server_2008 = pywbem.Uint16(76)
- Microsoft_Windows_Server_2008_64_Bit = pywbem.Uint16(77)
- FreeBSD_64_Bit = pywbem.Uint16(78)
- RedHat_Enterprise_Linux = pywbem.Uint16(79)
- RedHat_Enterprise_Linux_64_Bit = pywbem.Uint16(80)
- Solaris_64_Bit = pywbem.Uint16(81)
- SUSE = pywbem.Uint16(82)
- SUSE_64_Bit = pywbem.Uint16(83)
- SLES = pywbem.Uint16(84)
- SLES_64_Bit = pywbem.Uint16(85)
- Novell_OES = pywbem.Uint16(86)
- Novell_Linux_Desktop = pywbem.Uint16(87)
- Sun_Java_Desktop_System = pywbem.Uint16(88)
- Mandriva = pywbem.Uint16(89)
- Mandriva_64_Bit = pywbem.Uint16(90)
- TurboLinux = pywbem.Uint16(91)
- TurboLinux_64_Bit = pywbem.Uint16(92)
- Ubuntu = pywbem.Uint16(93)
- Ubuntu_64_Bit = pywbem.Uint16(94)
- Debian = pywbem.Uint16(95)
- Debian_64_Bit = pywbem.Uint16(96)
- Linux_2_4_x = pywbem.Uint16(97)
- Linux_2_4_x_64_Bit = pywbem.Uint16(98)
- Linux_2_6_x = pywbem.Uint16(99)
- Linux_2_6_x_64_Bit = pywbem.Uint16(100)
- Linux_64_Bit = pywbem.Uint16(101)
- Other_64_Bit = pywbem.Uint16(102)
- Microsoft_Windows_Server_2008_R2 = pywbem.Uint16(103)
- VMware_ESXi = pywbem.Uint16(104)
- Microsoft_Windows_7 = pywbem.Uint16(105)
- CentOS_32_bit = pywbem.Uint16(106)
- CentOS_64_bit = pywbem.Uint16(107)
- Oracle_Enterprise_Linux_32_bit = pywbem.Uint16(108)
- Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109)
- eComStation_32_bitx = pywbem.Uint16(110)
-
- class Remove(object):
- Not_installed = pywbem.Uint32(0)
- Successful_removal = pywbem.Uint32(1)
- Failed = pywbem.Uint32(2)
-
- class CommunicationStatus(object):
- Unknown = pywbem.Uint16(0)
- Not_Available = pywbem.Uint16(1)
- Communication_OK = pywbem.Uint16(2)
- Lost_Communication = pywbem.Uint16(3)
- No_Contact = pywbem.Uint16(4)
- # DMTF_Reserved = ..
- # Vendor_Reserved = 0x8000..
-
- class OperationalStatus(object):
- Unknown = pywbem.Uint16(0)
- Other = pywbem.Uint16(1)
- OK = pywbem.Uint16(2)
- Degraded = pywbem.Uint16(3)
- Stressed = pywbem.Uint16(4)
- Predictive_Failure = pywbem.Uint16(5)
- Error = pywbem.Uint16(6)
- Non_Recoverable_Error = pywbem.Uint16(7)
- Starting = pywbem.Uint16(8)
- Stopping = pywbem.Uint16(9)
- Stopped = pywbem.Uint16(10)
- In_Service = pywbem.Uint16(11)
- No_Contact = pywbem.Uint16(12)
- Lost_Communication = pywbem.Uint16(13)
- Aborted = pywbem.Uint16(14)
- Dormant = pywbem.Uint16(15)
- Supporting_Entity_in_Error = pywbem.Uint16(16)
- Completed = pywbem.Uint16(17)
- Power_Mode = pywbem.Uint16(18)
- Relocating = pywbem.Uint16(19)
- # DMTF_Reserved = ..
- # Vendor_Reserved = 0x8000..
-
- class OperatingStatus(object):
- Unknown = pywbem.Uint16(0)
- Not_Available = pywbem.Uint16(1)
- Servicing = pywbem.Uint16(2)
- Starting = pywbem.Uint16(3)
- Stopping = pywbem.Uint16(4)
- Stopped = pywbem.Uint16(5)
- Aborted = pywbem.Uint16(6)
- Dormant = pywbem.Uint16(7)
- Completed = pywbem.Uint16(8)
- Migrating = pywbem.Uint16(9)
- Emigrating = pywbem.Uint16(10)
- Immigrating = pywbem.Uint16(11)
- Snapshotting = pywbem.Uint16(12)
- Shutting_Down = pywbem.Uint16(13)
- In_Test = pywbem.Uint16(14)
- Transitioning = pywbem.Uint16(15)
- In_Service = pywbem.Uint16(16)
- # DMTF_Reserved = ..
- # Vendor_Reserved = 0x8000..
-
- class SoftwareElementState(object):
- Deployable = pywbem.Uint16(0)
- Installable = pywbem.Uint16(1)
- Executable = pywbem.Uint16(2)
- Running = pywbem.Uint16(3)
-
- class PrimaryStatus(object):
- Unknown = pywbem.Uint16(0)
- OK = pywbem.Uint16(1)
- Degraded = pywbem.Uint16(2)
- Error = pywbem.Uint16(3)
- # DMTF_Reserved = ..
- # Vendor_Reserved = 0x8000..
-
- class Install(object):
- Already_installed = pywbem.Uint32(0)
- Successful_installation = pywbem.Uint32(1)
- Failed = pywbem.Uint32(2)
-
diff --git a/src/software/openlmi/software/core/SoftwarePackageChecks.py b/src/software/openlmi/software/core/SoftwarePackageChecks.py
deleted file mode 100644
index 3b49128..0000000
--- a/src/software/openlmi/software/core/SoftwarePackageChecks.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""
-Just a common functionality related to LMI_SoftwarePackageChecks provider.
-"""
-
-import pywbem
-
-class Values(object):
- class Phase(object):
- In_State = pywbem.Uint16(0)
- Next_State = pywbem.Uint16(1)
diff --git a/src/software/openlmi/software/core/SystemCollection.py b/src/software/openlmi/software/core/SystemCollection.py
new file mode 100644
index 0000000..2d07130
--- /dev/null
+++ b/src/software/openlmi/software/core/SystemCollection.py
@@ -0,0 +1,73 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Common utilities concerning SystemSoftwareCollection provider.
+"""
+
+import pywbem
+
+from openlmi.common import cmpi_logging
+
+def get_path():
+ """@return instance name with prefilled properties"""
+ op = pywbem.CIMInstanceName(
+ classname="LMI_SystemSoftwareCollection",
+ namespace="root/cimv2")
+ op['InstanceID'] = "LMI:SystemSoftwareCollection"
+ return op
+
+@cmpi_logging.trace_function
+def check_path(env, collection, prop_name):
+ """
+ Checks instance name of SystemSoftwareCollection.
+ @param prop_name name of object name; used for error descriptions
+ """
+ if not isinstance(collection, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_collection = get_path()
+ ch = env.get_cimom_handle()
+ if collection.namespace != our_collection.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_collection.namespace))
+ if not ch.is_subclass(our_collection.namespace,
+ sub=collection.classname,
+ super=our_collection.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a sublass of %s" % (
+ prop_name, our_collection.classname))
+ if not 'InstanceID' in collection:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" is missing InstanceID key property", prop_name)
+ if collection['InstanceID'] != our_collection['InstanceID']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "InstanceID of \"%s\" does not match \"%s\"" %
+ prop_name, our_collection['InstanceID'])
+ return True
+
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether prop_name property of op object path is correct.
+ If not, an exception will be risen.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
diff --git a/src/software/openlmi/software/core/__init__.py b/src/software/openlmi/software/core/__init__.py
index 6ff965f..668e7be 100644
--- a/src/software/openlmi/software/core/__init__.py
+++ b/src/software/openlmi/software/core/__init__.py
@@ -18,3 +18,67 @@
#
# Authors: Michal Minar <miminar@redhat.com>
#
+
+"""
+Core functionality of particular providers.
+Each provider having functionality useful to others has a submodule
+in this subpackage with the same name except for LMI_ prefix.
+"""
+
+import pywbem
+from openlmi.common import cmpi_logging
+
+@cmpi_logging.trace_function
+def generate_references(
+ env,
+ object_name,
+ model,
+ filter_result_class_name,
+ filter_role,
+ filter_result_role,
+ keys_only,
+ handlers):
+ """
+ Function for generating references to object_name. It's supposed
+ to be used directly from references method of provider classes.
+ @param handlers is a list of tuples:
+ [ (role, class_name, handler), ... ]
+ where handler is a function, that will be called for matching
+ parameters, yielding instance(s/names). It accepts arguments:
+ env, object_name, model, keys_only.
+ """
+ if not isinstance(object_name, pywbem.CIMInstanceName):
+ raise TypeError("object_name must be a CIMInstanceName")
+ if not isinstance(model, pywbem.CIMInstance):
+ raise TypeError("model must be a CIMInstance")
+
+ ch = env.get_cimom_handle()
+ model.path.update(dict((t[0], None) for t in handlers))
+
+ try:
+ for i, (role, clsname, handler) in enumerate(handlers):
+ if ( ( filter_role
+ and filter_role.lower() != role.lower())
+ or not ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super=clsname)):
+ continue
+ other_results = list((htuple[0], htuple[1]) for htuple in (
+ handlers[:i] + handlers[i+1:]))
+ for res_role, res_clsname in other_results:
+ if ( ( not filter_result_role
+ or filter_result_role.lower() == res_role.lower())
+ and (not filter_result_class_name or ch.is_subclass(
+ object_name.namespace,
+ sub=filter_result_class_name,
+ super=res_clsname))):
+ break
+ else:
+ continue
+ # matches filters
+ for obj in handler(env, object_name, model, keys_only):
+ yield obj
+ except pywbem.CIMError as exc:
+ if exc.args[0] != pywbem.CIM_ERR_NOT_FOUND:
+ raise
+
diff --git a/src/software/openlmi/software/util/__init__.py b/src/software/openlmi/software/util/__init__.py
index 3392b1c..9b0ab01 100644
--- a/src/software/openlmi/software/util/__init__.py
+++ b/src/software/openlmi/software/util/__init__.py
@@ -25,18 +25,19 @@
import platform
import re
+import signal
RE_EVRA = re.compile(
- r'^(?P<epoch>\d+):(?P<ver>[^-]+)-(?P<rel>.+)\.(?P<arch>[^.]+)$')
+ r'^(?P<epoch>\d+):(?P<version>[^-]+)-(?P<release>.+)\.(?P<arch>[^.]+)$')
RE_NEVRA = re.compile(
- r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
- r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+ r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?P<arch>[^.]+))$')
RE_NEVRA_OPT_EPOCH = re.compile(
- r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<ver>[^-]+)'
- r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+ r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?P<arch>[^.]+))$')
RE_ENVRA = re.compile(
- r'^(?P<epoch>\d+):(?P<name>.+)-(?P<evra>(?P<ver>[^-]+)'
- r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+ r'^(?P<epoch>\d+):(?P<name>.+)-(?P<evra>(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?P<arch>[^.]+))$')
def _get_distname():
"""
@@ -56,7 +57,7 @@ def get_target_operating_system():
of CIM_SoftwareElement class and text is its testual representation.
"""
- system = platform.system()
+ system = platform.system()
if system.lower() == 'linux':
try:
val, dist = \
@@ -120,16 +121,16 @@ def nevra2filter(nevra):
else:
raise TypeError("nevra must be either string or regexp match object")
epoch = match.group("epoch")
- if not epoch or match.group("epoch") == "(none)":
+ if not epoch or match.group("epoch").lower() == "(none)":
epoch = "0"
- return { "name" : match.group("name")
- , "epoch" : epoch
- , "version" : match.group("ver")
- , "release" : match.group("rel")
- , "arch" : match.group("arch")
+ return { "name" : match.group("name")
+ , "epoch" : epoch
+ , "version" : match.group("version")
+ , "release" : match.group("release")
+ , "arch" : match.group("arch")
}
-def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
+def make_nevra(name, epoch, version, release, arch, with_epoch='NOT_ZERO'):
"""
@param with_epoch may be one of:
"NOT_ZERO" - include epoch only if it's not zero
@@ -144,7 +145,7 @@ def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
estr = epoch
if len(estr):
estr += ":"
- return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
+ return "%s-%s%s-%s.%s" % (name, estr, version, release, arch)
def pkg2nevra(pkg, with_epoch='NOT_ZERO'):
"""
@@ -152,3 +153,16 @@ def pkg2nevra(pkg, with_epoch='NOT_ZERO'):
"""
return make_nevra(pkg.name, pkg.epoch, pkg.version,
pkg.release, pkg.arch, with_epoch)
+
+def get_signal_name(signal_num):
+ """
+ @return name of signal for signal_num argument
+ """
+ if not isinstance(signal_num, (int, long)):
+ raise TypeError("signal_num must be an integer")
+ try:
+ return dict((v, k) for k, v in signal.__dict__.items())[signal_num]
+ except KeyError:
+ return "UNKNOWN_SIGNAL(%d)" % signal_num
+
+
diff --git a/src/software/openlmi/software/util/singletonmixin.py b/src/software/openlmi/software/util/singletonmixin.py
index 8051695..c252676 100644
--- a/src/software/openlmi/software/util/singletonmixin.py
+++ b/src/software/openlmi/software/util/singletonmixin.py
@@ -1,8 +1,9 @@
+#pylint: disable-all
"""
A Python Singleton mixin class that makes use of some of the ideas
found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
from it and you have a singleton. No code is required in
-subclasses to create singleton behavior -- inheritance from
+subclasses to create singleton behavior -- inheritance from
Singleton is all that is needed.
Singleton creation is threadsafe.
@@ -11,8 +12,8 @@ USAGE:
Just inherit from Singleton. If you need a constructor, include
an __init__() method in your class as you usually would. However,
-if your class is S, you instantiate the singleton using S.getInstance()
-instead of S(). Repeated calls to S.getInstance() return the
+if your class is S, you instantiate the singleton using S.get_instance()
+instead of S(). Repeated calls to S.get_instance() return the
originally-created instance.
For example:
@@ -21,8 +22,8 @@ class S(Singleton):
def __init__(self, a, b=1):
pass
-
-S1 = S.getInstance(1, b=3)
+
+S1 = S.get_instance(1, b=3)
Most of the time, that's all you need to know. However, there are some
@@ -30,46 +31,47 @@ other useful behaviors. Read on for a full description:
1) Getting the singleton:
- S.getInstance()
-
-returns the instance of S. If none exists, it is created.
+ S.get_instance()
+
+returns the instance of S. If none exists, it is created.
2) The usual idiom to construct an instance by calling the class, i.e.
S()
-
-is disabled for the sake of clarity.
-For one thing, the S() syntax means instantiation, but getInstance()
+is disabled for the sake of clarity.
+
+For one thing, the S() syntax means instantiation, but get_instance()
usually does not cause instantiation. So the S() syntax would
be misleading.
-Because of that, if S() were allowed, a programmer who didn't
+Because of that, if S() were allowed, a programmer who didn't
happen to notice the inheritance from Singleton (or who
wasn't fully aware of what a Singleton pattern
-does) might think he was creating a new instance,
+does) might think he was creating a new instance,
which could lead to very unexpected behavior.
So, overall, it is felt that it is better to make things clearer
by requiring the call of a class method that is defined in
-Singleton. An attempt to instantiate via S() will result
+Singleton. An attempt to instantiate via S() will result
in a SingletonException being raised.
3) Use __S.__init__() for instantiation processing,
-since S.getInstance() runs S.__init__(), passing it the args it has received.
+since S.get_instance() runs S.__init__(), passing it the args it has received.
-If no data needs to be passed in at instantiation time, you don't need S.__init__().
+If no data needs to be passed in at instantiation time,
+you don't need S.__init__().
4) If S.__init__(.) requires parameters, include them ONLY in the
-first call to S.getInstance(). If subsequent calls have arguments,
+first call to S.get_instance(). If subsequent calls have arguments,
a SingletonException is raised by default.
If you find it more convenient for subsequent calls to be allowed to
-have arguments, but for those argumentsto be ignored, just include
+have arguments, but for those argumentsto be ignored, just include
'ignoreSubsequent = True' in your class definition, i.e.:
class S(Singleton):
-
+
ignoreSubsequent = True
def __init__(self, a, b=1):
@@ -77,345 +79,392 @@ have arguments, but for those argumentsto be ignored, just include
5) For testing, it is sometimes convenient for all existing singleton
instances to be forgotten, so that new instantiations can occur. For that
-reason, a forgetAllSingletons() function is included. Just call
+reason, a _forget_all_singletons() function is included. Just call
+
+ _forget_all_singletons()
- forgetAllSingletons()
-
and it is as if no earlier instantiations have occurred.
-6) As an implementation detail, classes that inherit
+6) As an implementation detail, classes that inherit
from Singleton may not have their own __new__
-methods. To make sure this requirement is followed,
+methods. To make sure this requirement is followed,
an exception is raised if a Singleton subclass includ
es __new__. This happens at subclass instantiation
time (by means of the MetaSingleton metaclass.
-By Gary Robinson, grobinson@flyfi.com. No rights reserved --
+By Gary Robinson, grobinson@flyfi.com. No rights reserved --
placed in the public domain -- which is only reasonable considering
how much it owes to other people's code and ideas which are in the
-public domain. The idea of using a metaclass came from
-a comment on Gary's blog (see
-http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
+public domain. The idea of using a metaclass came from
+a comment on Gary's blog (see
+http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
Other improvements came from comments and email from other
people who saw it online. (See the blog post and comments
for further credits.)
Not guaranteed to be fit for any particular purpose. Use at your
-own risk.
+own risk.
"""
import threading
class SingletonException(Exception):
+ """
+ Base exception related to singleton handling.
+ """
pass
-_stSingletons = set()
-_lockForSingletons = threading.RLock()
-_lockForSingletonCreation = threading.RLock() # Ensure only one instance of each Singleton
- # class is created. This is not bound to the
- # individual Singleton class since we need to
- # ensure that there is only one mutex for each
- # Singleton class, which would require having
- # a lock when setting up the Singleton class,
- # which is what this is anyway. So, when any
- # Singleton is created, we lock this lock and
- # then we don't need to lock it again for that
- # class.
-
-def _createSingletonInstance(cls, lstArgs, dctKwArgs):
- _lockForSingletonCreation.acquire()
+_ST_SINGLETONS = set()
+_LOCK_FOR_SINGLETONS = threading.RLock()
+# Ensure only one instance of each Singleton class is created. This is not
+# bound to the _LOCK_FOR_SINGLETON_CREATION = threading.RLock() individual
+# Singleton class since we need to ensure that there is only one mutex for each
+# Singleton class, which would require having a lock when setting up the
+# Singleton class, which is what this is anyway. So, when any Singleton is
+# created, we lock this lock and then we don't need to lock it again for that
+# class.
+_LOCK_FOR_SINGLETON_CREATION = threading.RLock()
+
+def _create_singleton_instance(cls, lst_args, dct_kw_args):
+ """
+ Creates singleton instance and stores its class in set.
+ """
+ _LOCK_FOR_SINGLETON_CREATION.acquire()
try:
- if cls._isInstantiated(): # some other thread got here first
- return
-
+ if cls._is_instantiated(): # some other thread got here first
+ return
+
instance = cls.__new__(cls)
try:
- instance.__init__(*lstArgs, **dctKwArgs)
- except TypeError, e:
- if e.message.find('__init__() takes') != -1:
- raise SingletonException, 'If the singleton requires __init__ args, supply them on first call to getInstance().'
+ instance.__init__(*lst_args, **dct_kw_args)
+ except TypeError, exc:
+ if '__init__() takes' in exc.message:
+ raise SingletonException, (
+ 'If the singleton requires __init__ args,'
+ ' supply them on first call to get_instance().')
else:
raise
- cls.cInstance = instance
- _addSingleton(cls)
+ cls.c_instance = instance
+ _add_singleton(cls)
finally:
- _lockForSingletonCreation.release()
+ _LOCK_FOR_SINGLETON_CREATION.release()
-def _addSingleton(cls):
- _lockForSingletons.acquire()
+def _add_singleton(cls):
+ """
+ Adds class to singleton set.
+ """
+ _LOCK_FOR_SINGLETONS.acquire()
try:
- assert cls not in _stSingletons
- _stSingletons.add(cls)
+ assert cls not in _ST_SINGLETONS
+ _ST_SINGLETONS.add(cls)
finally:
- _lockForSingletons.release()
+ _LOCK_FOR_SINGLETONS.release()
-def _removeSingleton(cls):
- _lockForSingletons.acquire()
+def _remove_singleton(cls):
+ """
+ Removes class from singleton set.
+ """
+ _LOCK_FOR_SINGLETONS.acquire()
try:
- if cls in _stSingletons:
- _stSingletons.remove(cls)
+ if cls in _ST_SINGLETONS:
+ _ST_SINGLETONS.remove(cls)
finally:
- _lockForSingletons.release()
-
-def forgetAllSingletons():
- '''This is useful in tests, since it is hard to know which singletons need to be cleared to make a test work.'''
- _lockForSingletons.acquire()
+ _LOCK_FOR_SINGLETONS.release()
+
+def _forget_all_singletons():
+ '''
+ This is useful in tests, since it is hard to know which singletons need
+ to be cleared to make a test work.
+ '''
+ _LOCK_FOR_SINGLETONS.acquire()
try:
- for cls in _stSingletons.copy():
- cls._forgetClassInstanceReferenceForTesting()
+ for cls in _ST_SINGLETONS.copy():
+ cls._forget_class_instance_reference_for_testing()
# Might have created some Singletons in the process of tearing down.
# Try one more time - there should be a limit to this.
- iNumSingletons = len(_stSingletons)
- if len(_stSingletons) > 0:
- for cls in _stSingletons.copy():
- cls._forgetClassInstanceReferenceForTesting()
- iNumSingletons -= 1
- assert iNumSingletons == len(_stSingletons), 'Added a singleton while destroying ' + str(cls)
- assert len(_stSingletons) == 0, _stSingletons
+ i_num_singletons = len(_ST_SINGLETONS)
+ if len(_ST_SINGLETONS) > 0:
+ for cls in _ST_SINGLETONS.copy():
+ cls._forget_class_instance_reference_for_testing()
+ i_num_singletons -= 1
+ assert i_num_singletons == len(_ST_SINGLETONS), \
+ 'Added a singleton while destroying ' + str(cls)
+ assert len(_ST_SINGLETONS) == 0, _ST_SINGLETONS
finally:
- _lockForSingletons.release()
+ _LOCK_FOR_SINGLETONS.release()
class MetaSingleton(type):
- def __new__(metaclass, strName, tupBases, dct):
+ """
+ Metaclass for Singleton base class.
+ """
+ def __new__(mcs, str_name, tup_bases, dct):
if dct.has_key('__new__'):
raise SingletonException, 'Can not override __new__ in a Singleton'
- return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
-
- def __call__(cls, *lstArgs, **dictArgs):
- raise SingletonException, 'Singletons may only be instantiated through getInstance()'
-
+ return super(MetaSingleton, mcs).__new__(
+ mcs, str_name, tup_bases, dct)
+
+ def __call__(cls, *lst_args, **dictArgs):
+ raise SingletonException, \
+ 'Singletons may only be instantiated through get_instance()'
+
class Singleton(object):
+ """
+ Base class for all singletons.
+ """
__metaclass__ = MetaSingleton
-
- def getInstance(cls, *lstArgs, **dctKwArgs):
+
+ def get_instance(cls, *lst_args, **dct_kw_args):
"""
Call this to instantiate an instance or retrieve the existing instance.
If the singleton requires args to be instantiated, include them the first
- time you call getInstance.
+ time you call get_instance.
"""
- if cls._isInstantiated():
- if (lstArgs or dctKwArgs) and not hasattr(cls, 'ignoreSubsequent'):
- raise SingletonException, 'Singleton already instantiated, but getInstance() called with args.'
+ if cls._is_instantiated():
+ if ( (lst_args or dct_kw_args)
+ and not hasattr(cls, 'ignoreSubsequent')):
+ raise SingletonException, (
+ 'Singleton already instantiated, but get_instance()'
+ ' called with args.')
else:
- _createSingletonInstance(cls, lstArgs, dctKwArgs)
-
- return cls.cInstance
- getInstance = classmethod(getInstance)
-
- def _isInstantiated(cls):
- # Don't use hasattr(cls, 'cInstance'), because that screws things up if there is a singleton that
- # extends another singleton. hasattr looks in the base class if it doesn't find in subclass.
- return 'cInstance' in cls.__dict__
- _isInstantiated = classmethod(_isInstantiated)
+ _create_singleton_instance(cls, lst_args, dct_kw_args)
+
+ return cls.c_instance #pylint: disable=E1101
+ get_instance = classmethod(get_instance)
+
+ def _is_instantiated(cls):
+ """
+ Don't use hasattr(cls, 'c_instance'), because that screws things
+ up if there is a singleton that extends another singleton.
+ hasattr looks in the base class if it doesn't find in subclass.
+ """
+ return 'c_instance' in cls.__dict__
+ _is_instantiated = classmethod(_is_instantiated)
# This can be handy for public use also
- isInstantiated = _isInstantiated
+ isInstantiated = _is_instantiated
- def _forgetClassInstanceReferenceForTesting(cls):
+ def _forget_class_instance_reference_for_testing(cls):
"""
- This is designed for convenience in testing -- sometimes you
+ This is designed for convenience in testing -- sometimes you
want to get rid of a singleton during test code to see what
- happens when you call getInstance() under a new situation.
-
+ happens when you call get_instance() under a new situation.
+
To really delete the object, all external references to it
also need to be deleted.
"""
try:
- if hasattr(cls.cInstance, '_prepareToForgetSingleton'):
+ if hasattr(cls.c_instance, '_prepare_to_forget_singleton'):
# tell instance to release anything it might be holding onto.
- cls.cInstance._prepareToForgetSingleton()
- del cls.cInstance
- _removeSingleton(cls)
+ cls.c_instance._prepare_to_forget_singleton()
+ del cls.c_instance
+ _remove_singleton(cls)
except AttributeError:
- # run up the chain of base classes until we find the one that has the instance
- # and then delete it there
- for baseClass in cls.__bases__:
- if issubclass(baseClass, Singleton):
- baseClass._forgetClassInstanceReferenceForTesting()
- _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
-
-
-if __name__ == '__main__':
+ # run up the chain of base classes until we find the one that has
+ # the instance and then delete it there
+ for base_class in cls.__bases__:
+ if issubclass(base_class, Singleton):
+ base_class._forget_class_instance_reference_for_testing()
+ _forget_class_instance_reference_for_testing = classmethod(
+ _forget_class_instance_reference_for_testing)
+
+
+if __name__ == '__main__':
import unittest
import time
-
- class singletonmixin_Public_TestCase(unittest.TestCase):
- def testReturnsSameObject(self):
+
+ class SingletonMixinPublicTestCase(unittest.TestCase):
+ """
+ TestCase for singleton class.
+ """
+ def testReturnsSameObject(self): #pylint: disable=C0103
"""
- Demonstrates normal use -- just call getInstance and it returns a singleton instance
+ Demonstrates normal use -- just call get_instance and it returns a singleton instance
"""
-
- class A(Singleton):
+
+ class Foo(Singleton):
+ """Singleton child class."""
def __init__(self):
- super(A, self).__init__()
-
- a1 = A.getInstance()
- a2 = A.getInstance()
+ super(Foo, self).__init__()
+
+ a1 = Foo.get_instance()
+ a2 = Foo.get_instance()
self.assertEquals(id(a1), id(a2))
-
- def testInstantiateWithMultiArgConstructor(self):
+
+ def testInstantiateWithMultiArgConstructor(self):#pylint: disable=C0103
"""
If the singleton needs args to construct, include them in the first
call to get instances.
"""
-
- class B(Singleton):
-
+
+ class Bar(Singleton):
+ """Singleton child class."""
+
def __init__(self, arg1, arg2):
- super(B, self).__init__()
+ super(Bar, self).__init__()
self.arg1 = arg1
self.arg2 = arg2
-
- b1 = B.getInstance('arg1 value', 'arg2 value')
- b2 = B.getInstance()
+
+ b1 = Bar.get_instance('arg1 value', 'arg2 value')
+ b2 = Bar.get_instance()
self.assertEquals(b1.arg1, 'arg1 value')
self.assertEquals(b1.arg2, 'arg2 value')
self.assertEquals(id(b1), id(b2))
-
+
def testInstantiateWithKeywordArg(self):
-
- class B(Singleton):
-
+ """
+ Test instantiation with keyword arguments.
+ """
+
+ class Baz(Singleton):
+ """Singleton child class."""
def __init__(self, arg1=5):
- super(B, self).__init__()
+ super(Baz, self).__init__()
self.arg1 = arg1
-
- b1 = B.getInstance('arg1 value')
- b2 = B.getInstance()
+
+ b1 = Baz.get_instance('arg1 value')
+ b2 = Baz.get_instance()
self.assertEquals(b1.arg1, 'arg1 value')
self.assertEquals(id(b1), id(b2))
-
+
def testTryToInstantiateWithoutNeededArgs(self):
-
- class B(Singleton):
-
+ """
+ This tests, improper instantiation.
+ """
+
+ class Foo(Singleton):
+ """Singleton child class."""
def __init__(self, arg1, arg2):
- super(B, self).__init__()
+ super(Foo, self).__init__()
self.arg1 = arg1
self.arg2 = arg2
-
- self.assertRaises(SingletonException, B.getInstance)
-
+
+ self.assertRaises(SingletonException, Foo.get_instance)
+
def testPassTypeErrorIfAllArgsThere(self):
"""
- Make sure the test for capturing missing args doesn't interfere with a normal TypeError.
+ Make sure the test for capturing missing args doesn't interfere
+ with a normal TypeError.
"""
- class B(Singleton):
-
+ class Bar(Singleton):
+ """Singleton child class."""
def __init__(self, arg1, arg2):
- super(B, self).__init__()
+ super(Bar, self).__init__()
self.arg1 = arg1
self.arg2 = arg2
raise TypeError, 'some type error'
-
- self.assertRaises(TypeError, B.getInstance, 1, 2)
-
+
+ self.assertRaises(TypeError, Bar.get_instance, 1, 2)
+
def testTryToInstantiateWithoutGetInstance(self):
"""
Demonstrates that singletons can ONLY be instantiated through
- getInstance, as long as they call Singleton.__init__ during construction.
-
- If this check is not required, you don't need to call Singleton.__init__().
+ get_instance, as long as they call Singleton.__init__ during
+ construction.
+
+ If this check is not required, you don't need to call
+ Singleton.__init__().
"""
-
- class A(Singleton):
+
+ class A(Singleton):
def __init__(self):
super(A, self).__init__()
-
+
self.assertRaises(SingletonException, A)
-
+
def testDontAllowNew(self):
-
+
def instantiatedAnIllegalClass():
- class A(Singleton):
+ class A(Singleton):
def __init__(self):
super(A, self).__init__()
-
- def __new__(metaclass, strName, tupBases, dct):
- return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
-
+
+ def __new__(metaclass, str_name, tup_bases, dct):
+ return super(MetaSingleton, metaclass).__new__(
+ metaclass, str_name, tup_bases, dct)
+
self.assertRaises(SingletonException, instantiatedAnIllegalClass)
-
-
+
+
def testDontAllowArgsAfterConstruction(self):
- class B(Singleton):
-
+ class B(Singleton):
+
def __init__(self, arg1, arg2):
super(B, self).__init__()
self.arg1 = arg1
self.arg2 = arg2
-
- B.getInstance('arg1 value', 'arg2 value')
+
+ B.get_instance('arg1 value', 'arg2 value')
self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
-
+
def test_forgetClassInstanceReferenceForTesting(self):
- class A(Singleton):
+ class A(Singleton):
def __init__(self):
super(A, self).__init__()
- class B(A):
+ class B(A):
def __init__(self):
super(B, self).__init__()
-
- # check that changing the class after forgetting the instance produces
- # an instance of the new class
- a = A.getInstance()
+
+ # check that changing the class after forgetting the instance
+ # produces an instance of the new class
+ a = A.get_instance()
assert a.__class__.__name__ == 'A'
- A._forgetClassInstanceReferenceForTesting()
- b = B.getInstance()
+ A._forget_class_instance_reference_for_testing()
+ b = B.get_instance()
assert b.__class__.__name__ == 'B'
-
- # check that invoking the 'forget' on a subclass still deletes the instance
- B._forgetClassInstanceReferenceForTesting()
- a = A.getInstance()
- B._forgetClassInstanceReferenceForTesting()
- b = B.getInstance()
+
+ # check that invoking the 'forget' on a subclass still deletes
+ # the instance
+ B._forget_class_instance_reference_for_testing()
+ a = A.get_instance()
+ B._forget_class_instance_reference_for_testing()
+ b = B.get_instance()
assert b.__class__.__name__ == 'B'
-
+
def test_forgetAllSingletons(self):
# Should work if there are no singletons
- forgetAllSingletons()
-
+ _forget_all_singletons()
+
class A(Singleton):
ciInitCount = 0
def __init__(self):
super(A, self).__init__()
A.ciInitCount += 1
-
- A.getInstance()
+
+ A.get_instance()
self.assertEqual(A.ciInitCount, 1)
-
- A.getInstance()
+
+ A.get_instance()
self.assertEqual(A.ciInitCount, 1)
-
- forgetAllSingletons()
- A.getInstance()
+
+ _forget_all_singletons()
+ A.get_instance()
self.assertEqual(A.ciInitCount, 2)
-
+
def test_threadedCreation(self):
- # Check that only one Singleton is created even if multiple
- # threads try at the same time. If fails, would see assert in _addSingleton
+ # Check that only one Singleton is created even if multiple threads
+ # try at the same time. If fails, would see assert in _add_singleton
class Test_Singleton(Singleton):
def __init__(self):
super(Test_Singleton, self).__init__()
-
+
class Test_SingletonThread(threading.Thread):
def __init__(self, fTargetTime):
super(Test_SingletonThread, self).__init__()
self._fTargetTime = fTargetTime
self._eException = None
-
+
def run(self):
try:
fSleepTime = self._fTargetTime - time.time()
if fSleepTime > 0:
time.sleep(fSleepTime)
- Test_Singleton.getInstance()
- except Exception, e:
- self._eException = e
-
+ Test_Singleton.get_instance()
+ except Exception, exc:
+ self._eException = exc
+
fTargetTime = time.time() + 0.1
lstThreads = []
for _ in xrange(100):
@@ -429,80 +478,83 @@ if __name__ == '__main__':
eException = t._eException
if eException:
raise eException
-
+
def testNoInit(self):
"""
Demonstrates use with a class not defining __init__
"""
-
- class A(Singleton):
+
+ class A(Singleton):
pass
-
+
#INTENTIONALLY UNDEFINED:
#def __init__(self):
# super(A, self).__init__()
-
- A.getInstance() #Make sure no exception is raised
-
+
+ A.get_instance() #Make sure no exception is raised
+
def testMultipleGetInstancesWithArgs(self):
-
+
class A(Singleton):
-
+
ignoreSubsequent = True
-
+
def __init__(self, a, b=1):
pass
-
- a1 = A.getInstance(1)
- a2 = A.getInstance(2) # ignores the second call because of ignoreSubsequent
-
+
+ a1 = A.get_instance(1)
+ # ignores the second call because of ignoreSubsequent
+ a2 = A.get_instance(2)
+
class B(Singleton):
-
+
def __init__(self, a, b=1):
pass
-
- b1 = B.getInstance(1)
- self.assertRaises(SingletonException, B.getInstance, 2) # No ignoreSubsequent included
-
+
+ b1 = B.get_instance(1)
+ # No ignoreSubsequent included
+ self.assertRaises(SingletonException, B.get_instance, 2)
+
class C(Singleton):
-
+
def __init__(self, a=1):
pass
-
- c1 = C.getInstance(a=1)
- self.assertRaises(SingletonException, C.getInstance, a=2) # No ignoreSubsequent included
-
+
+ c1 = C.get_instance(a=1)
+ # No ignoreSubsequent included
+ self.assertRaises(SingletonException, C.get_instance, a=2)
+
def testInheritance(self):
- """
+ """
It's sometimes said that you can't subclass a singleton (see, for instance,
http://steve.yegge.googlepages.com/singleton-considered-stupid point e). This
test shows that at least rudimentary subclassing works fine for us.
"""
-
+
class A(Singleton):
-
- def setX(self, x):
+
+ def set_x(self, x):
self.x = x
-
+
def setZ(self, z):
raise NotImplementedError
-
+
class B(A):
-
- def setX(self, x):
+
+ def set_x(self, x):
self.x = -x
-
- def setY(self, y):
+
+ def set_y(self, y):
self.y = y
-
- a = A.getInstance()
- a.setX(5)
- b = B.getInstance()
- b.setX(5)
- b.setY(50)
+
+ a = A.get_instance()
+ a.set_x(5)
+ b = B.get_instance()
+ b.set_x(5)
+ b.set_y(50)
self.assertEqual((a.x, b.x, b.y), (5, -5, 50))
- self.assertRaises(AttributeError, eval, 'a.setY', {}, locals())
+ self.assertRaises(AttributeError, eval, 'a.set_y', {}, locals())
self.assertRaises(NotImplementedError, b.setZ, 500)
unittest.main()
-
+
diff --git a/src/software/openlmi/software/yumdb/__init__.py b/src/software/openlmi/software/yumdb/__init__.py
index d52b469..b5160b4 100644
--- a/src/software/openlmi/software/yumdb/__init__.py
+++ b/src/software/openlmi/software/yumdb/__init__.py
@@ -33,10 +33,12 @@ only accessor to yum api.
"""
import errno
+import inspect
import os
import re
import time
-from multiprocessing import Process, JoinableQueue, Queue
+from multiprocessing import Process, Queue #pylint: disable=W0404
+from pywbem.cim_provider2 import CIMProvider2
import Queue as TQueue # T as threaded
import threading
import yum
@@ -48,16 +50,30 @@ from openlmi.software.yumdb.packageinfo import PackageInfo
from openlmi.software.yumdb.packagecheck import PackageFile
from openlmi.software.yumdb.packagecheck import PackageCheck
from openlmi.software.yumdb.process import YumWorker
-from openlmi.software.util import singletonmixin
+from openlmi.software.yumdb.repository import Repository
+from openlmi.software.yumdb.util import DispatchingFormatter
+from openlmi.software.util import get_signal_name, singletonmixin
+
+# Maximum time in seconds to wait for a job to accomplish.
+# If timeout expires, spawned process is checked (it might
+# be possibly killed) and is respawned in case it's dead.
+MAX_JOB_WAIT_TIME = 30
# this may be used as an argument to YumWorker
YUM_WORKER_DEBUG_LOGGING_CONFIG = {
"version" : 1,
"formatters": {
+ # this is a message format for logging function/method calls
+ # it's manually set up in YumWorker's init method
"default": {
- "format" : "%(asctime)s %(levelname)s:%(module)s:"
- "%(funcName)s:%(lineno)d - %(message)s"
- }
+ "()": DispatchingFormatter,
+ "formatters" : {
+ "openlmi.software.yumdb.util.trace_function":
+ "%(asctime)s %(levelname)s:%(message)s"
+ },
+ "default" : "%(asctime)s %(levelname)s:%(module)s:"
+ "%(funcName)s:%(lineno)d - %(message)s"
+ },
},
"handlers": {
"file" : {
@@ -65,20 +81,128 @@ YUM_WORKER_DEBUG_LOGGING_CONFIG = {
"filename" : "/var/tmp/YumWorker.log",
"level" : "DEBUG",
"formatter": "default",
- }
+ },
},
- "root": {
- "level": "DEBUG",
- "handlers" : ["file"]
- }
+ "loggers" : {
+ "root": {
+ "level": "ERROR",
+ "handlers" : ["file"]
+ },
+ "openlmi.software.yumdb": {
+ "level" : "DEBUG",
+ "handlers" : ["file"],
+ "propagate" : False,
+ },
}
+}
+
+# *****************************************************************************
+# Utilities
+# *****************************************************************************
+def log_reply_error(job, reply):
+ """
+ Raises an exception in case of error occured in worker process
+ while processing job.
+ """
+ if isinstance(reply, (int, long)):
+ # asynchronous job
+ return
+ if not isinstance(reply, jobs.YumJob):
+ raise TypeError('expected instance of jobs.YumJob for reply, not "%s"' %
+ reply.__class__.__name__)
+ if reply.result == jobs.YumJob.RESULT_ERROR:
+ cmpi_logging.logger.error(
+ "YumDB: %s failed with error %s: %s",
+ job, reply.result_data[0].__name__, str(reply.result_data[1]))
+ cmpi_logging.logger.trace_warn(
+ "YumDB: %s exception traceback:\n%s%s: %s",
+ job, "".join(reply.result_data[2]),
+ reply.result_data[0].__name__, str(reply.result_data[1]))
+ reply.result_data[1].tb_printed = True
+ raise reply.result_data[1]
+ elif reply.result == jobs.YumJob.RESULT_TERMINATED:
+ cmpi_logging.logger.warn('YumDB: %s terminated', job)
+ else:
+ cmpi_logging.logger.debug('YumDB: %s completed with success', job)
+
+def _make_async_job(jobcls, *args, **kwargs):
+ """Creates asynchronous job, filling it wih some metadata."""
+ if not issubclass(jobcls, jobs.YumAsyncJob):
+ raise TypeError("jobcls must be a subclass of YumAsyncJob")
+ job = jobcls(*args, **kwargs)
+ if job.metadata is None:
+ job.metadata = {}
+ job.metadata['name'] = \
+ type(job).__name__[len('Yum'):] + ('-%d' % job.jobid)
+ frm = inspect.currentframe()
+ method_name = None
+ while ( frm is not None
+ and ( not 'self' in frm.f_locals
+ or not isinstance(frm.f_locals['self'], CIMProvider2))):
+ frm = frm.f_back
+ if frm is not None:
+ prov = frm.f_locals['self']
+ method_name = frm.f_code.co_name.lower()
+ if method_name.startswith('cim_method_'):
+ method_name = method_name[len('cim_method_'):]
+ if hasattr(prov, 'values'):
+ lowertocorrectcase = {
+ k.lower(): k for k in prov.values.__dict__ }
+ try:
+ method_name = lowertocorrectcase[method_name]
+ except KeyError:
+ pass
+ if method_name is not None:
+ job.metadata['method_name'] = method_name
+ return job
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def job_request(async=False):
+ """
+ Decorator factory for job entry points. They are YumDB methods.
+ All of them must return either job objects or jobid for asynchronous calls.
+ Job objects are processed by this decorator for caller to obtain only the
+ information he needs.
+
+ It wrapps them with logger wrapper and in case of asynchronous jobs,
+ it returns just the jobid.
+ """
+ def _decorator(method):
+ """
+ Decorator that just logs the method's call and returns job's result.
+ """
+ logged = cmpi_logging.trace_method(method)
+ def _new_func(self, *args, **kwargs):
+ """Wrapper for YumDB's method."""
+ return logged(self, *args, **kwargs).result_data
+ return _new_func
+
+ def _decorator_async(method):
+ """
+ Decorator for methods accepting async argument. In case of async=True,
+ the method returns jobid. Job's result is returned otherwise.
+ """
+ logged = cmpi_logging.trace_method(method)
+ def _new_func(self, *args, **kwargs):
+ """Wrapper for YumDB's method."""
+ callargs = inspect.getcallargs(method, self, *args, **kwargs)
+ result = logged(self, *args, **kwargs)
+ if callargs.get('async', False):
+ return result
+ else:
+ return result.result_data
+ return _new_func
+
+ return _decorator_async if async else _decorator
class YumDB(singletonmixin.Singleton):
"""
Context manager for accessing yum/rpm database.
All requests are bundled into jobs -- instances of jobs.YumJob and
sent to YumWorker for processing.
-
+
YumWorker is a separate process handling all calls to yum api.
Communication is done via queues (uplink and downlink).
Uplink is used to send jobs to YumWorker and downlink for obtaining
@@ -99,19 +223,23 @@ class YumDB(singletonmixin.Singleton):
# this is to inform Singleton, that __init__ should be called only once
ignoreSubsequent = True
- def __init__(self, *args, **kwargs): #pylint: disable=W0231
+ @cmpi_logging.trace_method
+ def __init__(self, **kwargs): #pylint: disable=W0231
"""
All arguments are passed to yum.YumBase constructor.
"""
self._process = None
- self._yum_args = (args, kwargs)
+ if kwargs is None:
+ kwargs = {}
+ self._yum_kwargs = kwargs
+
+ self._session_lock = threading.RLock()
+ self._session_level = 0
- # used to access _replies dictionary and _expected list
+ # used to guard access to _expected list and _process
self._reply_lock = threading.Lock()
# used to wait for job to be processed and received
self._reply_cond = threading.Condition(self._reply_lock)
- # { job_id : reply, ... }
- self._replies = {}
# ids of all expected jobs -- those to be processed by YumWorker
self._expected = []
cmpi_logging.logger.trace_info('YumDB: initialized')
@@ -120,49 +248,114 @@ class YumDB(singletonmixin.Singleton):
# Private methods
# *************************************************************************
@cmpi_logging.trace_method
- def _wait_for_reply(self, job):
+ def _handle_reply_timeout(self, job):
"""
- Blocks until job is processed by YumWorker and received.
+ This is called when timeout occurs while waiting on downlink queue for
+ reply. Delay can be caused by worker process's early termination (bug).
+ This handler tries to recover from such an situation.
+ """
+ if not self._worker.is_alive():
+ if self._worker.exitcode < 0:
+ cmpi_logging.logger.error("[jobid=%d] worker"
+ " process(pid=%d) killed by signal %s", job.jobid,
+ self._worker.pid, get_signal_name(-self._process.exitcode))
+ else:
+ cmpi_logging.logger.error("[jobid=%d] worker"
+ " process(pid=%d) is dead - exit code: %d",
+ job.jobid, self._process.pid, self._worker.exitcode)
+ with self._reply_lock:
+ self._process = None
+ cmpi_logging.logger.error(
+ "[jobid=%d] starting new worker process", job.jobid)
+ self._expected = []
+ if not isinstance(job, jobs.YumBeginSession):
+ with self._session_lock:
+ if self._session_level > 0:
+ cmpi_logging.logger.info('restoring session '
+ 'level=%d', self._session_level)
+ new_session_job = jobs.YumBeginSession()
+ self._worker.uplink.put(new_session_job)
+ reply = self._worker.downlink.get()
+ log_reply_error(new_session_job, reply)
+ self._worker.uplink.put(job)
+ self._expected.append(job.jobid)
+ # other waiting processes need to resend their requests
+ self._reply_cond.notifyAll()
+ else:
+ cmpi_logging.logger.info("[jobid=%d] process is running,"
+ " waiting some more", job.jobid)
- Only one thread can block on downlink channel to obtain reply. If
- it's reply for him, he takes it and leaves, otherwise he adds it to
- _replies dictionary and notifies other threads. This thread is the
- one, whose job appears as first in _expected list.
+ @cmpi_logging.trace_method
+ def _receive_reply(self, job):
+ """
+ Block on downlink queue to receive expected replies from worker
+ process. Only one thread can be executing this code at any time.
+
+ In case, that worker process terminated due to some error. Restart it
+ and resend all the job requests again.
+ """
+ while True:
+ cmpi_logging.logger.debug("[jobid=%d] blocking on downlink queue",
+ job.jobid)
+ try:
+ jobout = self._worker.downlink.get(
+ block=True, timeout=MAX_JOB_WAIT_TIME)
+ if jobout.jobid == job.jobid:
+ with self._reply_lock:
+ cmpi_logging.logger.debug(
+ "[jobid=%d] received desired reply", job.jobid)
+ self._expected.remove(job.jobid)
+ if len(self._expected):
+ self._reply_cond.notify()
+ return jobout
+ else:
+ # this should not happen
+ cmpi_logging.logger.error("[jobid=%d] received reply"
+ " for another thread (jobid=%d)",
+ job.jobid, jobout.jobid)
+ except TQueue.Empty:
+ cmpi_logging.logger.warn("[jobid=%d] wait for job reply timeout"
+ "(%d seconds) occured", job.jobid, MAX_JOB_WAIT_TIME)
+ self._handle_reply_timeout(job)
+
+ @cmpi_logging.trace_method
+ def _send_and_receive(self, job):
+ """
+ Sends a request to server and blocks until job is processed by
+ YumWorker and reply is received.
+
+ Only one thread can block on downlink channel to obtain reply. This
+ thread is the one, whose job appears as first in _expected list. Server
+ processes input jobs sequentially. That's why it's safe to presume,
+ that jobs are received in the same order as they were send. Thanks to
+ that we don't have to care about receiving replies for the other
+ waiting threads.
@return result of job
"""
with self._reply_lock:
- # until our job is not at the head of
+ self._worker.uplink.put(job)
+ if getattr(job, 'async', False) is True:
+ return job.jobid
self._expected.append(job.jobid)
- while job.jobid != self._expected[0]:
- if job.jobid in self._replies:
- self._expected.remove(job.jobid)
- return self._replies.pop(job.jobid)
- else:
+ while True:
+ if job.jobid not in self._expected:
+ # process terminated, resending job
+ cmpi_logging.logger.warn("[jobid=%d] job removed"
+ " from expected list, sending request again", job.jobid)
+ self._worker.uplink.put(job)
+ self._expected.append(job.jobid)
+ elif job.jobid == self._expected[0]:
+ # now it's our turn to block on downlink
+ break
+ else: # another thread blocks on downlink -> let's sleep
cmpi_logging.logger.debug(
- "[jobid=%d] another %s threads expecting reply,"
+ "[jobid=%d] another %d threads expecting reply,"
" suspending...", job.jobid, len(self._expected) - 1)
self._reply_cond.wait()
cmpi_logging.logger.debug(
"[jobid=%d] received reply, waking up", job.jobid)
- while True:
- cmpi_logging.logger.debug("[jobid=%d] blocking on downlink queue",
- job.jobid)
- jobid, reply = self._worker.downlink.get()
- with self._reply_lock:
- if jobid != job.jobid:
- cmpi_logging.logger.debug("[jobid=%d] received reply"
- " for another thread (jobid=%d)", job.jobid, jobid)
- self._replies[jobid] = reply
- self._reply_cond.notifyAll()
- else:
- cmpi_logging.logger.debug(
- "[jobid=%d] received desired reply", job.jobid)
- self._expected.remove(job.jobid)
- if len(self._expected):
- self._reply_cond.notify()
- break
- return reply
+ return self._receive_reply(job)
def _do_job(self, job):
"""
@@ -172,23 +365,10 @@ class YumDB(singletonmixin.Singleton):
(exception_type, exception_value, formated_traceback_as_string)
@return reply
"""
- cmpi_logging.logger.trace_verbose("YumDB: doing %s(id=%s) job",
- job.__class__.__name__, job.jobid)
- self._worker.uplink.put(job)
- reply = self._wait_for_reply(job)
- if isinstance(reply, tuple):
- cmpi_logging.logger.error(
- "YumDB: job %s(id=%s) failed with error %s: %s",
- job.__class__.__name__, job.jobid,
- reply[0].__name__, str(reply[1]))
- cmpi_logging.logger.trace_warn(
- "YumDB: job %s(id=%s) exception traceback:\n%s%s: %s",
- job.__class__.__name__, job.jobid, "".join(reply[2]),
- reply[0].__name__, str(reply[1]))
- reply[1].tb_printed = True
- raise reply[1]
- cmpi_logging.logger.trace_verbose("YumDB: job %s(id=%s) done",
- job.__class__.__name__, job.jobid)
+ cmpi_logging.logger.trace_verbose("YumDB: doing %s", job)
+ reply = self._send_and_receive(job)
+ log_reply_error(job, reply)
+ cmpi_logging.logger.trace_verbose("YumDB: job %s done", job.jobid)
return reply
@property
@@ -198,12 +378,14 @@ class YumDB(singletonmixin.Singleton):
"""
if self._process is None:
cmpi_logging.logger.trace_info("YumDB: starting YumWorker")
- uplink = JoinableQueue()
+ uplink = Queue()
downlink = Queue()
self._process = YumWorker(uplink, downlink,
- yum_args=self._yum_args[0], yum_kwargs=self._yum_args[1])
+ yum_kwargs=self._yum_kwargs)
#logging_config=YUM_WORKER_DEBUG_LOGGING_CONFIG)
self._process.start()
+ cmpi_logging.logger.trace_info(
+ "YumDB: YumWorker started with pid=%s", self._process.pid)
return self._process
# *************************************************************************
@@ -218,14 +400,24 @@ class YumDB(singletonmixin.Singleton):
@cmpi_logging.trace_method
def __enter__(self):
- self._do_job(jobs.YumBeginSession())
- cmpi_logging.logger.trace_info('YumDB: new session started')
- return self
+ with self._session_lock:
+ if self._session_level == 0:
+ self._do_job(jobs.YumBeginSession())
+ cmpi_logging.logger.trace_info('YumDB: new session started')
+ self._session_level += 1
+ cmpi_logging.logger.trace_info('YumDB: nested to session level=%d',
+ self._session_level)
+ return self
@cmpi_logging.trace_method
def __exit__(self, exc_type, exc_value, traceback):
- self._do_job(jobs.YumEndSession())
- cmpi_logging.logger.trace_info('YumDB: session ended')
+ with self._session_lock:
+ if self._session_level == 1:
+ self._do_job(jobs.YumEndSession())
+ cmpi_logging.logger.trace_info('YumDB: session ended')
+ cmpi_logging.logger.trace_info('YumDB: emerged from session'
+ ' level=%d', self._session_level)
+ self._session_level = max(self._session_level - 1, 0)
# *************************************************************************
# Public methods
@@ -235,32 +427,42 @@ class YumDB(singletonmixin.Singleton):
"""
Shut down the YumWorker process.
"""
- cmpi_logging.logger.info('YumDB: cleanup called')
- if self._process:
- cmpi_logging.logger.info('YumDB: terminating YumWorker')
- self._process.uplink.put(None) # terminating command
- self._process.uplink.join()
- self._process.join()
- cmpi_logging.logger.info('YumDB: YumWorker terminated')
- self._process = None
+ with self._reply_lock:
+ if self._process is not None:
+ cmpi_logging.logger.info('YumDB: terminating YumWorker')
+ self._process.uplink.put(None) # terminating command
+ self._process.join()
+ cmpi_logging.logger.info('YumDB: YumWorker terminated')
+ self._process = None
+ else:
+ cmpi_logging.logger.warn("YunDB: clean_up called, when process"
+ " not initialized!")
- @cmpi_logging.trace_method
+ # *************************************************************************
+ # Jobs with simple results
+ # *************************************************************************
+ @job_request()
def get_package_list(self, kind,
allow_duplicates=False,
- sort=False):
+ sort=False,
+ include_repos=None,
+ exclude_repos=None):
"""
- @param kind is one of: {"installed", "available", "all"}
+ @param kind is one of: jobs.YumGetPackageList.SUPPORTED_KINDS
@param allow_duplicates says, whether to list all found versions
of single package
@return [pkg1, pkg2, ...], pkgi is instance of yumdb.PackageInfo
"""
return self._do_job(jobs.YumGetPackageList(
- kind, allow_duplicates=allow_duplicates, sort=sort))
+ kind, allow_duplicates=allow_duplicates, sort=sort,
+ include_repos=include_repos, exclude_repos=exclude_repos))
- @cmpi_logging.trace_method
+ @job_request()
def filter_packages(self, kind,
allow_duplicates=False,
sort=False,
+ include_repos=None,
+ exclude_repos=None,
**filters):
"""
Similar to get_package_list(), but applies filter on packages.
@@ -268,51 +470,135 @@ class YumDB(singletonmixin.Singleton):
"""
return self._do_job(jobs.YumFilterPackages(
kind, allow_duplicates=allow_duplicates, sort=sort,
+ include_repos=include_repos, exclude_repos=exclude_repos,
**filters))
- @cmpi_logging.trace_method
- def install_package(self, pkg):
+ @job_request()
+ def get_repository_list(self, kind):
+ """
+ @param kind is one of: jobs.YumGetRepositoryList.SUPPORTED_KINDS
+ @param allow_duplicates says, whether to list all found versions
+ of single package
+ @return [pkg1, pkg2, ...], pkgi is instance of yumdb.Repository
+ """
+ return self._do_job(jobs.YumGetRepositoryList(kind))
+
+ @job_request()
+ def filter_repositories(self, kind, **filters):
+ """
+ Similar to get_repository_list(), but applies filter on packages.
+ @see yumdb.jobs.YumFilterRepositories job for supported filter keys
+ """
+ return self._do_job(jobs.YumFilterRepositories(kind, **filters))
+
+ @job_request()
+ def set_repository_enabled(self, repoid, enable):
+ """
+ Enable or disable repository.
+ @param enable is a boolean
+ """
+ return self._do_job(jobs.YumSetRepositoryEnabled(repoid, enable))
+
+ # *************************************************************************
+ # Asynchronous jobs
+ # *************************************************************************
+ @job_request(async=True)
+ def install_package(self, pkg, async=False, force=False):
"""
Install package.
@param pkg is an instance of PackageInfo obtained with
- get_package_list() or filter_packages(), which must be not installed
+ get_package_list() or filter_packages() or a valid nevra as string.
+ Package must not be installed if force is False.
"""
- return self._do_job(jobs.YumInstallPackage(pkg))
+ return self._do_job(_make_async_job(jobs.YumInstallPackage,
+ pkg, force=force, async=async))
- @cmpi_logging.trace_method
- def remove_package(self, pkg):
+ @job_request(async=True)
+ def remove_package(self, pkg, async=False):
"""
@param pkg is an instance of PackageInfo obtained with
get_package_list() or filter_packages(), which must be installed
"""
- return self._do_job(jobs.YumRemovePackage(pkg))
+ return self._do_job(_make_async_job(jobs.YumRemovePackage,
+ pkg, async=async))
- @cmpi_logging.trace_method
- def update_to_package(self, desired_pkg):
+ @job_request(async=True)
+ def update_to_package(self, desired_pkg, async=False):
"""
@param desired_pkg is an instance of PackageInfo,
which must be available
"""
- return self._do_job(jobs.YumUpdateToPackage(desired_pkg))
+ return self._do_job(_make_async_job(jobs.YumUpdateToPackage,
+ desired_pkg, async=async))
- @cmpi_logging.trace_method
+ @job_request(async=True)
def update_package(self, pkg,
+ async=False,
to_epoch=None,
to_version=None,
- to_release=None):
+ to_release=None,
+ force=False):
"""
@param pkg is an instance of PackageInfo, which must be installed
The other parameters filter candidate available packages for update.
"""
- return self._do_job(jobs.YumUpdatePackage(
- pkg, to_epoch, to_version, to_release))
+ return self._do_job(_make_async_job(jobs.YumUpdatePackage,
+ pkg, async, to_epoch, to_version, to_release, force=force))
- @cmpi_logging.trace_method
- def check_package(self, pkg):
+ @job_request(async=True)
+ def check_package(self, pkg, async=False):
"""
@param pkg is an instance of PackageInfo representing installed package
@return instance of yumdb.PackageCheck
"""
- return self._do_job(jobs.YumCheckPackage(pkg))
+ return self._do_job(_make_async_job(jobs.YumCheckPackage,
+ pkg, async=async))
+
+ @job_request(async=True)
+ def install_package_from_uri(self, uri,
+ async=False, update_only=False, force=False):
+ """
+ Install package from uri.
+ @param uri is either remote url or local path.
+ """
+ return self._do_job(jobs.YumInstallPackageFromURI(
+ uri, async, update_only, force=force))
+
+ # *************************************************************************
+ # Control of asynchronous jobs
+ # *************************************************************************
+ @job_request()
+ def get_job(self, jobid):
+ return self._do_job(jobs.YumJobGet(jobid))
+
+ @job_request()
+ def get_job_list(self):
+ return self._do_job(jobs.YumJobGetList())
+
+ @job_request()
+ def get_job_by_name(self, name):
+ return self._do_job(jobs.YumJobGetByName(name))
+
+ @job_request()
+ def set_job_priority(self, jobid, priority):
+ return self._do_job(jobs.YumJobSetPriority(jobid, priority))
+
+ @job_request()
+ def update_job(self, jobid, **kwargs):
+ return self._do_job(jobs.YumJobUpdate(jobid, **kwargs))
+
+ @job_request()
+ def reschedule_job(self, jobid,
+ delete_on_completion, time_before_removal):
+ return self._do_job(jobs.YumJobReschedule(jobid,
+ delete_on_completion, time_before_removal))
+
+ @job_request()
+ def delete_job(self, jobid):
+ return self._do_job(jobs.YumJobDelete(jobid))
+
+ @job_request()
+ def terminate_job(self, jobid):
+ return self._do_job(jobs.YumJobTerminate(jobid))
diff --git a/src/software/openlmi/software/yumdb/errors.py b/src/software/openlmi/software/yumdb/errors.py
index ae18608..101c40b 100644
--- a/src/software/openlmi/software/yumdb/errors.py
+++ b/src/software/openlmi/software/yumdb/errors.py
@@ -23,21 +23,69 @@
Exceptions raisable by YumWorker.
"""
-class DatabaseLockError(Exception):
+class YumDBError(Exception):
+ """Base class for all errors under yumdb package."""
+
+class DatabaseLockError(YumDBError):
"""Raised, when the yum database can not be locked."""
pass
-class TransactionError(Exception):
+
+class TransactionError(YumDBError):
"""Base exception representing yum transaction processing error."""
pass
class TransactionBuildFailed(TransactionError):
"""Raised, when transaction building fails."""
pass
+class PackageAlreadyInstalled(TransactionError):
+ """Raised, when trying to install already installed package."""
+ def __init__(self, pkg):
+ TransactionError.__init__(self,
+ 'Package "%s" is already installed.' % pkg)
+class PackageOpenError(TransactionError):
+ """Raised, when trying to open package obtained from URI."""
+ def __init__(self, pkg, msg):
+ TransactionError.__init__(self,
+ 'Failed to open package "%s": %s' % (pkg, msg))
class TransactionExecutionFailed(TransactionError):
"""Raised, when YumBase.doTransaction() method fails."""
pass
-class UnknownJob(Exception):
- """Raised, when no handler is available for given job on worker."""
+
+class PackageError(YumDBError):
pass
-class PackageNotFound(Exception):
+class PackageNotFound(PackageError):
"""Raised, when requested package could not be found."""
pass
+class PackageNotInstalled(PackageError):
+ """Raised, when requested package is not installed for desired action."""
+ def __init__(self, pkg):
+ PackageError.__init__(self, 'Package "%s" is not installed.' % pkg)
+
+class RepositoryError(YumDBError):
+ pass
+class RepositoryNotFound(RepositoryError):
+ """Raised, when requested repository cound not be found."""
+ def __init__(self, repoid):
+ RepositoryError.__init__(self, "No such repository: %s" % repoid)
+class RepositoryChangeError(RepositoryError):
+ """Raised, when modification of repository failed."""
+ pass
+
+class JobError(YumDBError):
+ pass
+class UnknownJob(JobError):
+ """Raised, when no handler is available for given job on worker."""
+ pass
+class InvalidURI(JobError):
+ """Raised, when passed uri is not a valid one."""
+ def __init__(self, uri):
+ JobError.__init__(self, "Invalid uri: \"%s\"" % uri)
+class InvalidNevra(JobError):
+ pass
+class JobControlError(JobError):
+ pass
+class JobNotFound(JobControlError):
+ def __init__(self, target):
+ JobControlError.__init__(self, "job %s could not be found" % target)
+class InvalidJobState(JobControlError):
+ pass
+
diff --git a/src/software/openlmi/software/yumdb/jobmanager.py b/src/software/openlmi/software/yumdb/jobmanager.py
new file mode 100644
index 0000000..872e81f
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/jobmanager.py
@@ -0,0 +1,413 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+This is a module for JobManager which is a separate thread of
+YumWorker process. It keeps a cache of asynchronous jobs and handles
+input and output queues.
+"""
+import heapq
+import inspect
+import logging
+import Queue
+import sys
+import threading
+import time
+import traceback
+
+from openlmi.software.yumdb import errors, jobs
+from openlmi.software.yumdb.util import trace_function
+
+# Minimum time to keep asynchronous job in cache after completion. In seconds.
+MINIMUM_TIME_BEFORE_REMOVAL = 10
+
+LOG = None
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def job_handler(job_from_target=True):
+ """
+ Decorator for JobManager methods serving as handlers for control jobs.
+
+ Decorator locks the job_lock of manager's instance.
+ """
+ def _wrapper_jft(method):
+ """
+ It consumes "target" keyword argument (which is job's id) and makes
+ it an instance of YumJob. The method is then called with "job" argument
+ instead of "target".
+ """
+ logged = trace_function(method)
+
+ def _new_func(self, *args, **kwargs):
+ """Wrapper around method."""
+ if 'target' in kwargs:
+ kwargs['job'] = kwargs.pop('target')
+ callargs = inspect.getcallargs(method, self, *args, **kwargs)
+ target = callargs.pop('job')
+ with self._job_lock: #pylint: disable=W0212
+ if not target in self._async_jobs: #pylint: disable=W0212
+ raise errors.JobNotFound(target)
+ job = self._async_jobs[target] #pylint: disable=W0212
+ callargs['job'] = job
+ return logged(**callargs)
+ return _new_func
+
+ def _simple_wrapper(method):
+ """Just locks the job lock."""
+ def _new_func(self, *args, **kwargs):
+ """Wrapper around method."""
+ with self._job_lock: #pylint: disable=W0212
+ return method(self, *args, **kwargs)
+ return _new_func
+
+ if job_from_target:
+ return _wrapper_jft
+ else:
+ return _simple_wrapper
+
+class JobManager(threading.Thread):
+ """
+ Separate thread for managing queue of jobs requested by client.
+ There are three kinds of jobs, that are handled differently:
+ * asynchronous - kept in _async_jobs dictionary until job is
+ deleted by request or it expires;
+ no reply is sent to client upon job's completion
+ * synchronous - reply is sent to client after job's completion;
+ no reference to the job is kept afterwards
+ * job control - they are not enqueued in _job_queue for YumWorker
+ to process, but are handled directly and in the FIFO order
+
+ Both asynchronous and synchronous jobs are enqueued in _job_queue
+ for YumWorker to obtain them. It's a priority queue sorting jobs by their
+ priority.
+ """
+ # enumeration of actions, that may be enqueued in calendar
+ ACTION_REMOVE = 0
+
+ ACTION_NAMES = ['remove']
+
+ def __init__(self, queue_in, queue_out):
+ threading.Thread.__init__(self, name="JobManager")
+ self._queue_in = queue_in
+ self._queue_out = queue_out
+ self._terminate = False
+
+ # (time, jobid, action)
+ self._calendar = []
+ # {jobid : job}
+ self._async_jobs = {}
+
+ # lock for critical access to _calendar, _async_jobs and _job_queue
+ self._job_lock = threading.RLock()
+ # priority queue of jobs that are processed by YumWorker
+ self._job_queue = []
+ # condition for YumWorker waiting on empty _job_queue
+ self._job_enqueued = threading.Condition(self._job_lock)
+
+ # *************************************************************************
+ # Private methods
+ # *************************************************************************
+ @trace_function
+ def _control_job(self, job):
+ """
+ Function dispatching job to handler for particular YumJob subclass.
+ """
+ try:
+ handler = {
+ # these are from YumDB client
+ jobs.YumJobGetList : self._handle_get_list,
+ jobs.YumJobGet : self._handle_get,
+ jobs.YumJobGetByName : self._handle_get_by_name,
+ jobs.YumJobSetPriority : self._handle_set_priority,
+ jobs.YumJobReschedule : self._handle_reschedule,
+ jobs.YumJobUpdate : self._handle_update,
+ jobs.YumJobDelete : self._handle_delete,
+ jobs.YumJobTerminate : self._handle_terminate,
+ }[job.__class__]
+ LOG.info("processing control job %s", str(job))
+ except KeyError:
+ raise errors.UnknownJob("No handler for job \"%s\"." %
+ job.__class__.__name__)
+ return handler(**job.job_kwargs)
+
+ @trace_function
+ def _enqueue_job(self, job):
+ """
+ Insert incoming job into _job_queue.
+ """
+ if isinstance(job, jobs.YumJobControl):
+ result = job.RESULT_SUCCESS
+ job.start()
+ try:
+ data = self._control_job(job)
+ except Exception: #pylint: disable=W0703
+ result = job.RESULT_ERROR
+ data = sys.exc_info()
+ data = (data[0], data[1], traceback.format_tb(data[2]))
+ LOG.exception("control job %s failed", job)
+ job.finish(result, data)
+ LOG.debug("sending reply for %s: (%s, %s)", job,
+ job.ResultNames[job.result], job.result_data)
+ self._queue_out.put(job)
+ else:
+ if job is None:
+ LOG.debug('received terminating command')
+ self._terminate = True
+ LOG.debug('job %s enqued for YumWorker to handle', job)
+ heapq.heappush(self._job_queue, job)
+ if getattr(job, 'async', False) is True:
+ self._async_jobs[job.jobid] = job
+ self._job_enqueued.notify()
+
+ @trace_function
+ def _schedule_event(self, after, jobid, action):
+ """
+ Enqueue event into calendar. Event consists of time, jobid and
+ action.
+ """
+ schedule_at = time.time() + after
+ for (sched, jid, act) in self._calendar:
+ if jid == jobid and act == action:
+ if sched <= schedule_at: # same event already scheduled
+ return
+ # schedule it for early time
+ LOG.debug('rescheduling action %s on job %d to take place'
+ ' after %d seconds (instead of %d)',
+ self.ACTION_NAMES[action], jid, after,
+ sched - schedule_at + after)
+ self._calendar.remove((sched, jid, act))
+ self._calendar.append((schedule_at, jid, act))
+ heapq.heapify(self._calendar)
+ return
+ LOG.debug('scheduling action %s on job %d to take place after '
+ ' %d seconds', self.ACTION_NAMES[action], jobid, after)
+ heapq.heappush(self._calendar, (schedule_at, jobid, action))
+
+ @trace_function
+ def _run_event(self, jobid, action):
+ """
+ Process event from calendar.
+ """
+ if action == self.ACTION_REMOVE:
+ with self._job_lock:
+ del self._async_jobs[jobid]
+ else:
+ msg = "unsupported action: %s" % action
+ raise ValueError(msg)
+
+ # *************************************************************************
+ # Job handlers
+ # *************************************************************************
+ @job_handler()
+ def _handle_get(self, job): #pylint: disable=R0201
+ """@return job object"""
+ return job
+
+ @job_handler(False)
+ def _handle_get_list(self):
+ """@return list of all asynchronous jobs"""
+ with self._job_lock:
+ return sorted(self._async_jobs.values())
+
+ @job_handler(False)
+ def _handle_get_by_name(self, target):
+ """@return job object filtered by name"""
+ for job in self._async_jobs.values():
+ if 'name' in job.metadata and target == job.metadata['name']:
+ return job
+ raise errors.JobNotFound(target)
+
+ @job_handler()
+ def _handle_set_priority(self, job, new_priority):
+ """
+ Modify job's priority and updates its position in queue.
+ @return modified job object
+ """
+ if not isinstance(new_priority, (int, long)):
+ raise TypeError('priority must be an integer')
+ if job.priority != new_priority:
+ job.update(priority=new_priority)
+ if job in self._job_queue:
+ heapq.heapify(self._job_queue)
+ return job
+
+ @job_handler()
+ def _handle_reschedule(self, job,
+ delete_on_completion,
+ time_before_removal):
+ """
+ Changes job's schedule for its deletion.
+ """
+ if ( job.delete_on_completion == delete_on_completion
+ and job.time_before_removal == time_before_removal):
+ return
+ if job.finished and job.delete_on_completion:
+ for i, event in enumerate(self._calendar):
+ if event[1] == job.jobid and event[2] == self.ACTION_REMOVE:
+ del self._calendar[i]
+ heapq.heapify(self._calendar)
+ break
+ if delete_on_completion:
+ schedule_at = time_before_removal
+ if job.finished:
+ schedule_at = job.finished + schedule_at - time.time()
+ self._schedule_event(schedule_at, job.jobid, self.ACTION_REMOVE)
+ job.delete_on_completion = delete_on_completion
+ job.time_before_removal = time_before_removal
+ return job
+
+ @job_handler()
+ def _handle_update(self, job, data): #pylint: disable=R0201
+ """
+ Updates any job metadata.
+ """
+ job.update(**data)
+ return job
+
+ @job_handler()
+ def _handle_delete(self, job):
+ """
+ Deletes finished asynchronous job.
+ """
+ if not job.finished:
+ raise errors.InvalidJobState(
+ 'can not delete unfinished job "%s"' % job)
+ try:
+ self._job_queue.remove(job)
+ heapq.heapify(self._job_queue)
+ LOG.debug('job "%s" removed from queue', job)
+ except ValueError:
+ LOG.debug('job "%s" not started and not enqueued', job)
+ del self._async_jobs[job.jobid]
+ return job
+
+ @job_handler()
+ def _handle_terminate(self, job):
+ """
+ Terminates not started job.
+ """
+ if job.started and not job.finished:
+ raise errors.InvalidJobState('can not kill running job "%s"' % job)
+ if job.finished:
+ raise errors.InvalidJobState('job "%s" already finished' % job)
+ self._job_queue.remove(job)
+ heapq.heapify(self._job_queue)
+ job.finish(result=job.RESULT_TERMINATED)
+ LOG.info('terminated not started job "%s"', job)
+ return job
+
+ # *************************************************************************
+ # Public properties
+ # *************************************************************************
+ @property
+ def queue_in(self):
+ """Incoming queue for YumJob instances."""
+ return self._queue_in
+
+ @property
+ def queue_out(self):
+ """Output queue for results."""
+ return self._queue_out
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ @trace_function
+ def finish_job(self, job, result, result_data):
+ """
+ This should be called for any job by YumWorker after the job is
+ processed.
+
+ If the job is synchronous, reply is send at once. Otherwise the result
+ is stored for later client's query in the job itself.
+ """
+ with self._job_lock:
+ if job.state != job.RUNNING:
+ raise errors.InvalidJobState(
+ 'can not finish not started job "%s"' % job)
+ job.finish(result, result_data)
+ if getattr(job, 'async', False):
+ if job.delete_on_completion:
+ schedule_at = max( job.time_before_removal
+ , MINIMUM_TIME_BEFORE_REMOVAL)
+ self._schedule_event(schedule_at, job.jobid,
+ self.ACTION_REMOVE)
+ else:
+ LOG.debug("sending reply for %s: (%s, %s)", job,
+ job.ResultNames[job.result], job.result_data)
+ self._queue_out.put(job)
+ return job
+
+ @trace_function
+ def get_job(self, block=True, timeout=None):
+ """
+ Method supposed to be used only by YumWorker. It pops the first job
+ from _job_queue, starts it and returns it.
+ """
+ start = time.time()
+ with self._job_lock:
+ if len(self._job_queue) == 0 and not block:
+ raise Queue.Empty
+ while len(self._job_queue) == 0:
+ if timeout:
+ LOG.debug('waiting for job for %s seconds' % timeout)
+ self._job_enqueued.wait(timeout)
+ if len(self._job_queue) == 0:
+ now = time.time()
+ if timeout > now - start:
+ raise Queue.Empty
+ job = heapq.heappop(self._job_queue)
+ if job is not None:
+ job.start()
+ return job
+
+ def run(self):
+ """The entry point of thread."""
+ global LOG #pylint: disable=W0603
+ LOG = logging.getLogger(__name__)
+ LOG.info("%s thread started", self.name)
+
+ while self._terminate is False:
+ try:
+ timeout = None
+ with self._job_lock:
+ if len(self._calendar) > 0:
+ timeout = self._calendar[0][0] - time.time()
+ LOG.debug('waiting on input queue for job%s',
+ (' with timeout %s' % timeout) if timeout else '')
+ job = self._queue_in.get(timeout=timeout)
+ with self._job_lock:
+ self._enqueue_job(job)
+ while not self._queue_in.empty():
+ # this won't throw
+ self._enqueue_job(self._queue_in.get_nowait())
+
+ except Queue.Empty:
+ with self._job_lock:
+ while ( len(self._calendar)
+ and self._calendar[0][0] < time.time()):
+ _, jobid, action = heapq.heappop(self._calendar)
+ LOG.info('running action %s on job(id=%d)',
+ self.ACTION_NAMES[action], jobid)
+ self._run_event(jobid, action)
+ LOG.info('%s thread terminating', self.name)
+
diff --git a/src/software/openlmi/software/yumdb/jobs.py b/src/software/openlmi/software/yumdb/jobs.py
index 28f4ca3..a46a2ca 100644
--- a/src/software/openlmi/software/yumdb/jobs.py
+++ b/src/software/openlmi/software/yumdb/jobs.py
@@ -23,24 +23,45 @@
Define job classes representing kinds of jobs of worker process.
"""
+import os
import threading
+import time
+import yum
from openlmi.software import util
+from openlmi.software.yumdb import errors
from openlmi.software.yumdb.packageinfo import PackageInfo
+from openlmi.software.yumdb.repository import Repository
+
+DEFAULT_JOB_PRIORITY = 10
+# in seconds
+DEFAULT_TIME_BEFORE_REMOVAL = 60 * 5
class YumJob(object): #pylint: disable=R0903
"""
Base class for any job, that is processable by YumWorker process.
- It contains at least a jobid attribute, that must be unique for
+ It contains jobid attribute, that must be unique for
each job, it's counted from zero a incremented after each creation.
+
+ metadata attribute typically contain:
+ name - name of job, that is modifiable by user
+ method_name - name of provider's method, that lead to creation of job
"""
- __slots__ = ('jobid', )
+ __slots__ = ( 'jobid', 'created', 'started', 'finished', 'last_change'
+ , 'priority', 'result', 'result_data')
# jobs can be created concurrently from multiple threads, that's
# why we need to make its creation thread safe
_JOB_ID_LOCK = threading.Lock()
_JOB_ID = 0
+ # job state enumeration
+ NEW, RUNNING, COMPLETED, TERMINATED, EXCEPTION = range(5)
+ # job result enumeration
+ RESULT_SUCCESS, RESULT_TERMINATED, RESULT_ERROR = range(3)
+
+ ResultNames = ("success", "terminated", "error")
+
@staticmethod
def _get_job_id():
"""
@@ -53,35 +74,261 @@ class YumJob(object): #pylint: disable=R0903
YumJob._JOB_ID += 1
return val
- def __init__(self):
+ @classmethod
+ def handle_ignore_job_props(cls):
+ """
+ @return set of job properties, that does not count as job's handler
+ arguments - job handler does not care fore metadata, jobid, priority,
+ etc...
+ """
+ return set(YumJob.__slots__)
+
+ def __init__(self, priority=10):
+ if not isinstance(priority, (int, long)):
+ raise TypeError("priority must be integer")
self.jobid = self._get_job_id()
+ self.started = None
+ self.finished = None
+ self.priority = priority
+ self.created = time.time()
+ self.last_change = self.created
+ self.result = None
+ self.result_data = None
+
+ @property
+ def state(self):
+ """
+ @return integer representing job's state
+ """
+ if not self.started:
+ return self.NEW
+ if not self.finished:
+ return self.RUNNING
+ if self.result == self.RESULT_ERROR:
+ return self.EXCEPTION
+ if self.result == self.RESULT_TERMINATED:
+ return self.TERMINATED
+ return self.COMPLETED
@property
def job_kwargs(self):
"""
- Jobs are in worker represented be methods with arguments.
- Those can be obtained from job by calling this property.
+ Jobs are in worker handled in handlers specific for each subclass.
+ These handlers are methods of worker. They accepts concrete arguments
+ that can be obtained from job by invoking this property.
@return dictionary of keyword arguments of job
"""
kwargs = {}
cls = self.__class__
while not cls in (YumJob, object):
for slot in cls.__slots__:
- if not slot in kwargs:
+ if ( not slot in kwargs
+ and not slot in cls.handle_ignore_job_props()):
kwargs[slot] = getattr(self, slot)
cls = cls.__bases__[0]
- kwargs.pop('jobid', None)
+ for prop in YumJob.__slots__:
+ kwargs.pop(prop, None)
return kwargs
+ def start(self):
+ """Modify the state of job to RUNNING."""
+ if self.started:
+ raise errors.InvalidJobState("can not start already started job")
+ self.started = time.time()
+ self.last_change = self.started
+
+ def finish(self, result, data=None):
+ """
+ Modify the state of job to one of {COMPLETED, EXCEPTION, TERMINATED}.
+ Depending on result parameter.
+ """
+ if not self.started and result != self.RESULT_TERMINATED:
+ raise errors.InvalidJobState("can not finish not started job")
+ self.finished = time.time()
+ if result == self.RESULT_TERMINATED:
+ self.started = self.finished
+ self.result = result
+ self.result_data = data
+ self.last_change = self.finished
+
+ def update(self, **kwargs):
+ """Change job's properties."""
+ change = False
+ for key, value in kwargs.items():
+ if getattr(self, key) != value:
+ setattr(self, key, value)
+ change = True
+ if change is True:
+ self.last_change = time.time()
+
+ def __eq__(self, other):
+ return self.__class__ is other.__class__ and self.jobid == other.jobid
+
+ def __ne__(self, other):
+ return ( self.__class__ is not other.__class__
+ or self.jobid != other.jobid)
+
+ def __lt__(self, other):
+ """
+ JobControl jobs have the highest priority.
+ """
+ return ( ( isinstance(self, YumJobControl)
+ and not isinstance(other, YumJobControl))
+ or ( self.priority < other.priority
+ or ( self.priority == other.priority
+ and ( self.jobid < other.jobid
+ or ( self.jobid == other.jobid
+ and (self.created < other.created))))))
+
+ def __cmp__(self, other):
+ if ( isinstance(self, YumJobControl)
+ and not isinstance(other, YumJobControl)):
+ return -1
+ if ( not isinstance(self, YumJobControl)
+ and isinstance(other, YumJobControl)):
+ return 1
+ if self.priority < other.priority:
+ return -1
+ if self.priority > other.priority:
+ return 1
+ if self.jobid < other.jobid:
+ return -1
+ if self.jobid > other.jobid:
+ return 1
+ if self.created < other.created:
+ return -1
+ if self.created > other.created:
+ return 1
+ return 0
+
+ def __str__(self):
+ return "%s(id=%d,p=%d)" % (
+ self.__class__.__name__, self.jobid, self.priority)
+
def __getstate__(self):
ret = self.job_kwargs
- ret.update(jobid=self.jobid)
+ for prop in self.handle_ignore_job_props():
+ ret[prop] = getattr(self, prop)
return ret
def __setstate__(self, state):
for k, value in state.items():
setattr(self, k, value)
+class YumAsyncJob(YumJob): #pylint: disable=R0903
+ """
+ Base class for jobs, that support asynchronnous execution.
+ No reply is sent upon job completition or error. The results are
+ kept on server.
+ """
+ __slots__ = ( 'async'
+ , 'delete_on_completion'
+ , 'time_before_removal'
+ , 'metadata')
+
+ @classmethod
+ def handle_ignore_job_props(cls):
+ return YumJob.handle_ignore_job_props().union(YumAsyncJob.__slots__)
+
+ def __init__(self, priority=10, async=False, metadata=None):
+ YumJob.__init__(self, priority)
+ self.async = bool(async)
+ self.delete_on_completion = True
+ self.time_before_removal = DEFAULT_TIME_BEFORE_REMOVAL
+ if metadata is None and self.async is True:
+ metadata = {}
+ self.metadata = metadata
+
+ def __str__(self):
+ return "%s(id=%d,p=%d%s%s)" % (
+ self.__class__.__name__, self.jobid,
+ self.priority,
+ ',async' if self.async else '',
+ (',name="%s"'%self.metadata['name'])
+ if self.metadata and 'name' in self.metadata else '')
+
+ def update(self, **kwargs):
+ if 'metadata' in kwargs:
+ self.metadata.update(kwargs.pop('metadata'))
+ return YumJob.update(self, **kwargs)
+
+# *****************************************************************************
+# Job control funtions
+# *****************************************************************************
+class YumJobControl(YumJob): #pylint: disable=R0903
+ """Base class for any job used for asynchronous jobs management."""
+ pass
+
+class YumJobGetList(YumJobControl): #pylint: disable=R0903
+ """Request for obtaining list of all asynchronous jobs."""
+ pass
+
+class YumJobOnJob(YumJobControl):
+ """
+ Base class for any control job acting upon particular asynchronous job.
+ """
+ __slots__ = ('target', )
+ def __init__(self, target):
+ YumJobControl.__init__(self)
+ if not isinstance(target, (int, long)):
+ raise TypeError("target must be an integer")
+ self.target = target
+
+class YumJobGet(YumJobOnJob): #pylint: disable=R0903
+ """Get job object by its id."""
+ pass
+
+class YumJobGetByName(YumJobOnJob): #pylint: disable=R0903
+ """Get job object by its name property."""
+ def __init__(self, name):
+ YumJobOnJob.__init__(self, -1)
+ self.target = name
+
+class YumJobSetPriority(YumJobOnJob): #pylint: disable=R0903
+ """Change priority of job."""
+ __slots__ = ('new_priority', )
+
+ def __init__(self, target, priority):
+ YumJobOnJob.__init__(self, target)
+ self.new_priority = priority
+
+class YumJobUpdate(YumJobOnJob): #pylint: disable=R0903
+ """Update job's metadata."""
+ __slots__ = ('data', )
+ FORBIDDEN_PROPERTIES = (
+ 'async', 'jobid', 'created', 'started', 'priority', 'finished',
+ 'delete_on_completion', 'time_before_removal', 'last_change')
+
+ def __init__(self, target, **kwargs):
+ YumJobOnJob.__init__(self, target)
+ assert not set.intersection(
+ set(YumJobUpdate.FORBIDDEN_PROPERTIES), set(kwargs))
+ self.data = kwargs
+
+class YumJobReschedule(YumJobOnJob): #pylint: disable=R0903
+ """Change the schedule of job's deletion."""
+ __slots__ = ('delete_on_completion', 'time_before_removal')
+ def __init__(self, target, delete_on_completion, time_before_removal):
+ YumJobOnJob.__init__(self, target)
+ if not isinstance(time_before_removal, (int, long, float)):
+ raise TypeError("time_before_removal must be float")
+ self.delete_on_completion = bool(delete_on_completion)
+ self.time_before_removal = time_before_removal
+
+class YumJobDelete(YumJobOnJob): #pylint: disable=R0903
+ """Delete job - can only be called on finished job."""
+ pass
+
+class YumJobTerminate(YumJobOnJob): #pylint: disable=R0903
+ """
+ Can only be called on not yet started job.
+ Running job can not be terminated.
+ """
+ pass
+
+# *****************************************************************************
+# Yum API functions
+# *****************************************************************************
class YumBeginSession(YumJob): #pylint: disable=R0903
"""
Begin session on YumWorker which ensures that yum database is locked
@@ -100,27 +347,58 @@ class YumGetPackageList(YumJob): #pylint: disable=R0903
"""
Job requesing a list of packages.
Arguments:
- kind - supported values are in SUPPORTED_KINDS tuple
+ kind - supported values are in SUPPORTED_KINDS tuple
+ * installed lists all installed packages; more packages with
+ the same name can be installed varying in their architecture
+ * avail_notinst lists all available, not installed packages;
+ allow_duplicates must be True to include older packages (but still
+ available)
+ * avail_reinst lists all installed packages, that are available;
+ package can be installed, but not available anymore due to updates
+ of repository, where only the newest packages are kept
+ * available lists a union of avail_notinst and avail_reinst
+ * all lists union of installed and avail_notinst
+
allow_duplicates - whether multiple packages can be present
in result for single (name, arch) of package differing
in their version
+ sort - whether to sort packages by nevra
+
+ include_repos - either a string passable to RepoStorage.enableRepo()
+ or a list of repository names, that will be temporared enabled before
+ listing packages; this is applied after disabling of repositories
+
+ exclude_repos - either a string passable to RepoStorage.disableRepo()
+ or a list of repository names, that will be temporared disabled before
+ listing packages; this is applied before enabling of repositories
+
Worker replies with [pkg1, pkg2, ...].
"""
- __slots__ = ('kind', 'allow_duplicates', 'sort')
+ __slots__ = ('kind', 'allow_duplicates', 'sort', 'include_repos',
+ 'exclude_repos')
- SUPPORTED_KINDS = ('installed', 'available', 'all')
+ SUPPORTED_KINDS = ( 'installed', 'available', 'avail_reinst'
+ , 'avail_notinst', 'all')
- def __init__(self, kind, allow_duplicates, sort=False):
+ def __init__(self, kind, allow_duplicates, sort=False,
+ include_repos=None, exclude_repos=None):
YumJob.__init__(self)
if not isinstance(kind, basestring):
raise TypeError("kind must be a string")
if not kind in self.SUPPORTED_KINDS:
raise ValueError("kind must be one of {%s}" %
", ".join(self.SUPPORTED_KINDS))
+ for arg in ('include_repos', 'exclude_repos'):
+ val = locals()[arg]
+ if ( not val is None
+ and not isinstance(arg, (tuple, list, basestring))):
+ raise TypeError("expected list or string for %s" % arg)
self.kind = kind
self.allow_duplicates = bool(allow_duplicates)
self.sort = bool(sort)
+ self.include_repos = include_repos
+ self.exclude_repos = exclude_repos
class YumFilterPackages(YumGetPackageList): #pylint: disable=R0903
"""
@@ -128,7 +406,7 @@ class YumFilterPackages(YumGetPackageList): #pylint: disable=R0903
filter on packages.
Arguments (plus those in YumGetPackageList):
name, epoch, version, release, arch, nevra, envra, evra
-
+
Some of those are redundant, but filtering is optimized for
speed, so supplying all of them won't affect performance.
@@ -136,21 +414,23 @@ class YumFilterPackages(YumGetPackageList): #pylint: disable=R0903
"""
__slots__ = (
'name', 'epoch', 'version', 'release', 'arch',
- 'nevra', 'envra', 'evra')
+ 'nevra', 'envra', 'evra', 'repoid')
def __init__(self, kind, allow_duplicates,
- sort=False,
+ sort=False, include_repos=None, exclude_repos=None,
name=None, epoch=None, version=None,
release=None, arch=None,
nevra=None, evra=None,
- envra=None):
+ envra=None,
+ repoid=None):
if nevra is not None and not util.RE_NEVRA.match(nevra):
raise ValueError("Invalid nevra: %s" % nevra)
if evra is not None and not util.RE_EVRA.match(evra):
raise ValueError("Invalid evra: %s" % evra)
if envra is not None and not util.RE_ENVRA.match(evra):
raise ValueError("Invalid envra: %s" % envra)
- YumGetPackageList.__init__(self, kind, allow_duplicates, sort)
+ YumGetPackageList.__init__(self, kind, allow_duplicates, sort,
+ include_repos=include_repos, exclude_repos=exclude_repos)
self.name = name
self.epoch = None if epoch is None else str(epoch)
self.version = version
@@ -159,28 +439,44 @@ class YumFilterPackages(YumGetPackageList): #pylint: disable=R0903
self.nevra = nevra
self.evra = evra
self.envra = envra
+ self.repoid = repoid
-class YumSpecificPackageJob(YumJob): #pylint: disable=R0903
+class YumSpecificPackageJob(YumAsyncJob): #pylint: disable=R0903
"""
- Abstract job taking instance of yumdb.PackageInfo as argument.
+ Abstract job taking instance of yumdb.PackageInfo as argument or
+ package's nevra.
Arguments:
- pkg - plays different role depending on job subclass
+ pkg - plays different role depending on job subclass;
+ can also be a nevra
"""
__slots__ = ('pkg', )
- def __init__(self, pkg):
- if not isinstance(pkg, PackageInfo):
- raise TypeError("pkg must be instance of PackageInfo")
- YumJob.__init__(self)
+ def __init__(self, pkg, async=False, metadata=None):
+ if isinstance(pkg, basestring):
+ if not util.RE_NEVRA_OPT_EPOCH.match(pkg):
+ raise errors.InvalidNevra('not a valid nevra "%s"' % pkg)
+ elif not isinstance(pkg, PackageInfo):
+ raise TypeError("pkg must be either string or instance"
+ " of PackageInfo")
+ YumAsyncJob.__init__(self, async=async, metadata=metadata)
self.pkg = pkg
class YumInstallPackage(YumSpecificPackageJob): #pylint: disable=R0903
"""
Job requesting installation of specific package.
pkg argument should be available.
+ Arguments:
+ pkg - same as in YumSpecificPackageJob
+ force is a boolean saying:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
Worker replies with new instance of package.
"""
- pass
+ __slots__ = ('force', )
+ def __init__(self, pkg, async=False, force=False, metadata=None):
+ YumSpecificPackageJob.__init__(
+ self, pkg, async=async, metadata=metadata)
+ self.force = bool(force)
class YumRemovePackage(YumSpecificPackageJob): #pylint: disable=R0903
"""
@@ -205,30 +501,133 @@ class YumUpdatePackage(YumSpecificPackageJob): #pylint: disable=R0903
candidate packages to ones with specific evr.
Arguments:
to_epoch, to_version, to_release
+ force is a boolean, that has meaning only when update_only is False:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
The arguments more given, the more complete filter of candidates.
Worker replies with new instance of package.
"""
- __slots__ = ('to_epoch', 'to_version', 'to_release')
+ __slots__ = ('to_epoch', 'to_version', 'to_release', 'force')
- def __init__(self, pkg,
- to_epoch=None, to_version=None, to_release=None):
+ def __init__(self, pkg, async=False,
+ to_epoch=None, to_version=None, to_release=None, force=False,
+ metadata=None):
if not isinstance(pkg, PackageInfo):
raise TypeError("pkg must be instance of yumdb.PackageInfo")
- YumSpecificPackageJob.__init__(self, pkg)
+ YumSpecificPackageJob.__init__(
+ self, pkg, async=async, metadata=metadata)
self.to_epoch = to_epoch
self.to_version = to_version
self.to_release = to_release
+ self.force = bool(force)
class YumCheckPackage(YumSpecificPackageJob): #pylint: disable=R0903
"""
Request verification information for instaled package and its files.
-
+
Worker replies with new instance of yumdb.PackageCheck.
"""
- def __init__(self, pkg):
- YumSpecificPackageJob.__init__(self, pkg)
+ def __init__(self, pkg, async=False, metadata=None):
+ YumSpecificPackageJob.__init__(self, pkg, async=async,
+ metadata=metadata)
if not pkg.installed:
raise ValueError("package must be installed to check it")
+class YumInstallPackageFromURI(YumAsyncJob): #pylint: disable=R0903
+ """
+ Job requesting installation of specific package from URI.
+ Arguments:
+ uri is either a path to rpm package on local filesystem or url
+ of rpm stored on remote host
+ update_only is a boolean:
+ True -> install the package only if the older version is installed
+ False -> install the package if it's not already installed
+ force is a boolean, that has meaning only when update_only is False:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
+
+ Worker replies with new instance of package.
+ """
+ __slots__ = ('uri', 'update_only', "force")
+ def __init__(self, uri, async=False, update_only=False, force=False,
+ metadata=None):
+ if not isinstance(uri, basestring):
+ raise TypeError("uri must be a string")
+ if uri.startswith('file://'):
+ uri = uri[len('file://'):]
+ if not yum.misc.re_remote_url(uri) and not os.path.exists(uri):
+ raise errors.InvalidURI(uri)
+ YumAsyncJob.__init__(self, async=async, metadata=metadata)
+ self.uri = uri
+ self.update_only = bool(update_only)
+ self.force = bool(force)
+
+class YumGetRepositoryList(YumJob): #pylint: disable=R0903
+ """
+ Job requesing a list of repositories.
+ Arguments:
+ kind - supported values are in SUPPORTED_KINDS tuple
+
+ Worker replies with [repo1, repo2, ...].
+ """
+ __slots__ = ('kind', )
+
+ SUPPORTED_KINDS = ('all', 'enabled', 'disabled')
+
+ def __init__(self, kind):
+ YumJob.__init__(self)
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+ if not kind in self.SUPPORTED_KINDS:
+ raise ValueError("kind must be one of {%s}" %
+ ", ".join(self.SUPPORTED_KINDS))
+ self.kind = kind
+
+class YumFilterRepositories(YumGetRepositoryList): #pylint: disable=R0903
+ """
+ Job similar to YumGetRepositoryList, but allowing to specify
+ filter on packages.
+ Arguments (plus those in YumGetRepositoryList):
+ name, gpg_check, repo_gpg_check
+
+ Some of those are redundant, but filtering is optimized for
+ speed, so supplying all of them won't affect performance.
+
+ Worker replies with [repo1, repo2, ...].
+ """
+ __slots__ = ('repoid', 'gpg_check', 'repo_gpg_check')
+
+ def __init__(self, kind,
+ repoid=None, gpg_check=None, repo_gpg_check=None):
+ YumGetRepositoryList.__init__(self, kind)
+ self.repoid = repoid
+ self.gpg_check = None if gpg_check is None else bool(gpg_check)
+ self.repo_gpg_check = (
+ None if repo_gpg_check is None else bool(repo_gpg_check))
+
+class YumSpecificRepositoryJob(YumJob): #pylint: disable=R0903
+ """
+ Abstract job taking instance of yumdb.Repository as argument.
+ Arguments:
+ repoid - plays different role depending on job subclass
+ """
+ __slots__ = ('repoid', )
+ def __init__(self, repoid):
+ if not isinstance(repoid, Repository):
+ raise TypeError("repoid must be instance of yumdb.Repository")
+ YumJob.__init__(self)
+ self.repoid = repoid
+
+class YumSetRepositoryEnabled(YumSpecificRepositoryJob):#pylint: disable=R0903
+ """
+ Job allowing to enable or disable repository.
+ Arguments:
+ enable - boolean representing next state
+ """
+ __slots__ = ('enable', )
+ def __init__(self, repoid, enable):
+ YumSpecificRepositoryJob.__init__(self, repoid)
+ self.enable = bool(enable)
+
diff --git a/src/software/openlmi/software/yumdb/packagecheck.py b/src/software/openlmi/software/yumdb/packagecheck.py
index 3b49164..4cdc5ee 100644
--- a/src/software/openlmi/software/yumdb/packagecheck.py
+++ b/src/software/openlmi/software/yumdb/packagecheck.py
@@ -89,17 +89,17 @@ class PackageCheck(object):
Metadata for package concerning verification.
It contains metadata for each file installed in "files" attribute.
"""
- __slots__ = ("pkgid", "file_checksum_type", "files")
+ __slots__ = ("objid", "file_checksum_type", "files")
- def __init__(self, pkgid, file_checksum_type, files=None):
+ def __init__(self, objid, file_checksum_type, files=None):
"""
- @param pkgid is an in of original yum package object, which is used
+ @param objid is an in of original yum package object, which is used
by server for subsequent operations on this package requested by client
"""
if files is not None and not isinstance(
files, (list, tuple, set, dict)):
raise TypeError("files must be an iterable container")
- self.pkgid = pkgid
+ self.objid = objid
self.file_checksum_type = file_checksum_type
if not isinstance(files, dict):
self.files = OrderedDict()
diff --git a/src/software/openlmi/software/yumdb/packageinfo.py b/src/software/openlmi/software/yumdb/packageinfo.py
index dd23ac3..bfacc5d 100644
--- a/src/software/openlmi/software/yumdb/packageinfo.py
+++ b/src/software/openlmi/software/yumdb/packageinfo.py
@@ -36,23 +36,23 @@ class PackageInfo(object):
-- results in segfaults.
To speed up looking up of original yum package object on server, an
- atribute "pkgid" is provided.
+ atribute "objid" is provided.
"""
__slots__ = (
- "pkgid",
+ "objid",
"name", "epoch", "version", "release", "architecture",
'summary', 'description', 'license', 'group', 'vendor',
- 'size',
+ "repoid", 'size',
'installed', # boolean
- 'install_time' # datetime instance
+ 'install_time' # datetime instance
)
- def __init__(self, pkgid, name, epoch, version, release, arch, **kwargs):
+ def __init__(self, objid, name, epoch, version, release, arch, **kwargs):
"""
- @param pkgid is an in of original yum package object, which is used
+ @param objid is an in of original yum package object, which is used
by server for subsequent operations on this package requested by client
"""
- self.pkgid = pkgid
+ self.objid = objid
self.name = name
self.epoch = epoch
self.version = version
@@ -63,6 +63,7 @@ class PackageInfo(object):
self.license = kwargs.pop('license', None)
self.group = kwargs.pop('group', None)
self.vendor = kwargs.pop('vendor', None)
+ self.repoid = kwargs.pop("repoid", None)
self.size = kwargs.pop('size', None)
if self.size is not None and not isinstance(self.size, (int, long)):
raise TypeError('size must be an integer')
@@ -113,7 +114,7 @@ class PackageInfo(object):
that uniquelly identify package in database
"""
return dict((k, getattr(self, k)) for k in (
- 'name', 'epoch', 'version', 'release', 'arch'))
+ 'name', 'epoch', 'version', 'release', 'arch', 'repoid'))
# *************************************************************************
# Public methods
@@ -151,6 +152,15 @@ class PackageInfo(object):
for k, value in state.items():
setattr(self, k, value)
+ def __eq__(self, other):
+ return ( self.name == other.name
+ and self.version == other.version
+ and self.release == other.release
+ and self.arch == other.arch
+ and self.epoch == other.epoch
+ and ( (self.repoid is None or other.repoid is None)
+ or (self.repoid == other.repoid)))
+
def make_package_from_db(pkg):
"""
Create instance of PackageInfo from instance of
@@ -158,8 +168,8 @@ def make_package_from_db(pkg):
@return instance of PackageInfo
"""
metadata = dict((k, getattr(pkg, k)) for k in (
- 'summary', 'description', 'license', 'group', 'vendor',
- 'size'))
+ 'summary', 'description', 'license', 'group', 'vendor', 'size',
+ 'repoid'))
if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
metadata['installed'] = True
metadata['install_time'] = datetime.fromtimestamp(pkg.installtime)
diff --git a/src/software/openlmi/software/yumdb/process.py b/src/software/openlmi/software/yumdb/process.py
index 9c30a29..0b0c4c1 100644
--- a/src/software/openlmi/software/yumdb/process.py
+++ b/src/software/openlmi/software/yumdb/process.py
@@ -24,14 +24,15 @@ Module holding the code of separate process accessing the YUM API.
"""
import errno
-import inspect
from itertools import chain
import logging
from multiprocessing import Process
+import os
import Queue as TQueue # T as threaded
import sys
import time
import traceback
+from urlgrabber.grabber import default_grabber
import weakref
import yum
@@ -40,6 +41,9 @@ from openlmi.software.yumdb import errors
from openlmi.software.yumdb import jobs
from openlmi.software.yumdb import packageinfo
from openlmi.software.yumdb import packagecheck
+from openlmi.software.yumdb import repository
+from openlmi.software.yumdb.jobmanager import JobManager
+from openlmi.software.yumdb.util import trace_function, setup_logging
# *****************************************************************************
# Constants
@@ -48,19 +52,11 @@ from openlmi.software.yumdb import packagecheck
FREE_DATABASE_TIMEOUT = 60
LOCK_WAIT_INTERVAL = 0.5
RPMDB_PATH = '/var/lib/rpm/Packages'
+LOG = None
# *****************************************************************************
# Utilities
# ****************************************************************************
-def _logger():
- """
- Returns logger for this module, when first needed.
- @return logger specific for this process
- """
- if not hasattr(_logger, "logger"):
- _logger.logger = logging.getLogger(__name__)
- return _logger.logger
-
def _get_package_filter_function(filters):
"""
@param filters is a dictionary, where keys are package property
@@ -73,115 +69,127 @@ def _get_package_filter_function(filters):
filters = dict((k, value) for k, value in filters.items()
if value is not None)
+ match = None
if "nevra" in filters:
- def _cmp_nevra(pkg):
- """@return True if pkg matches nevra filter"""
- value = '%s-%s:%s-%s.%s' % (
- pkg.name,
- "0" if not pkg.epoch or pkg.epoch == "(none)"
- else pkg.epoch,
- pkg.version, pkg.release, pkg.arch)
- return value == filters["nevra"]
- return _cmp_nevra
-
+ match = util.RE_NEVRA.match(filters["nevra"])
elif "envra" in filters:
- def _cmp_envra(pkg):
- """@return True if pkg matches envra filter"""
- value = '%s:%s-%s-%s.%s' % (
- "0" if not pkg.epoch or pkg.epoch == "(none)"
- else pkg.epoch,
- pkg.name,
- pkg.version, pkg.release, pkg.arch)
- return value == filters["envra"]
- return _cmp_envra
-
- else:
- if "evra" in filters:
- for prop_name in ("epoch", "version", "release", "epoch"):
- filters.pop(prop_name, None)
- filter_list = []
- # properties are sorted by their filtering ability
- # (the most unprobable property, that can match, comes first)
- for prop_name in ("evra", "name", "version", "epoch",
- "release", "arch"):
- if not prop_name in filters:
- continue
- filter_list.append((prop_name, filters.pop(prop_name)))
- def _cmp_props(pkg):
- """@return True if pkg matches properies filter"""
- return all(getattr(pkg, p) == v for p, v in filter_list)
- return _cmp_props
+ match = util.RE_ENVRA.match(filters["envra"])
+ if match is not None:
+ for attr in ("name", "epoch", "version", "release", "arch"):
+ match_attr = attr
+ filters[attr] = match.group(match_attr)
+ filters.pop('nevra', None)
+ filters.pop('envra', None)
+ elif "evra" in filters:
+ for prop_name in ("epoch", "version", "release", "epoch"):
+ filters.pop(prop_name, None)
+ filter_list = []
+ # properties are sorted by their filtering ability
+ # (the most unprobable property, that can match, comes first)
+ for prop_name in ("evra", "name", "version", "epoch",
+ "release", "repoid", "arch"):
+ if not prop_name in filters:
+ continue
+ filter_list.append((prop_name, filters.pop(prop_name)))
+ def _cmp_props(pkg):
+ """@return True if pkg matches properies filter"""
+ return all(getattr(pkg, p) == v for p, v in filter_list)
+ return _cmp_props
+
+class RepoFilterSetter(object):
+ """
+ A context manager, that will set a repository filter lasting
+ as long as the object itself.
+ """
+ def __init__(self, yum_base, include_repos=None, exclude_repos=None):
+ if not isinstance(yum_base, yum.YumBase):
+ raise TypeError("yum_base must be a YumBase instance")
+ self._yum_base = yum_base
+ self._include = include_repos
+ self._exclude = exclude_repos
+ # after __enter__ this will be dictionary containing (
+ # repoid, enabled) pairs
+ self._prev_states = None
+
+ def __enter__(self):
+ self._prev_states = { r.id: r.enabled
+ for r in self._yum_base.repos.repos.values()}
+ if isinstance(self._exclude, (list, tuple, set)):
+ exclude = ",".join(self._exclude)
+ else:
+ exclude = self._exclude
+ # set of repositories, that were affected
+ repos = set()
+ if exclude:
+ repos.update(self._yum_base.repos.disableRepo(exclude))
+ LOG.info('disabling repositories: [%s]', ", ".join(repos))
+ if isinstance(self._include, (list, tuple, set)):
+ include = ",".join(self._include)
+ else:
+ include = self._include
+ if include:
+ affected = self._yum_base.repos.enableRepo(include)
+ LOG.info('enabling repositories: [%s]', ", ".join(affected))
+ repos.update(affected)
+ for repoid, prev_enabled in self._prev_states.items():
+ if ( repoid not in repos
+ or ( bool(prev_enabled)
+ is bool(self._yum_base.repos.getRepo(repoid).enabled))):
+ # keep only manipulated repositories
+ del self._prev_states[repoid]
+ if len(self._prev_states):
+ for repoid in (r for r, v in self._prev_states.items() if v):
+ self._yum_base.pkgSack.sacks.pop(repoid, None)
+ self._yum_base.repos.populateSack()
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ # restore previous repository states
+ if len(self._prev_states):
+ LOG.info('restoring repositories: [%s]',
+ ", ".join(self._prev_states.keys()))
+ for repoid, enabled in self._prev_states.items():
+ repo = self._yum_base.repos.getRepo(repoid)
+ if enabled:
+ repo.enable()
+ else:
+ repo.disable()
+ for repoid in (r for r, v in self._prev_states.items() if not v):
+ self._yum_base.pkgSack.sacks.pop(repoid, None)
+ self._yum_base.repos.populateSack()
# *****************************************************************************
# Decorators
# *****************************************************************************
-def _trace_function(func):
- """
- Decorator for logging entries and exits of function or method.
- """
- if not inspect.ismethod(func) and not inspect.isfunction(func):
- raise TypeError("func must be a function")
-
- def _print_value(val):
- """
- Used here for printing function arguments. Shortens the output
- string, if that would be too long.
- """
- if isinstance(val, list):
- if len(val) < 2:
- return str(val)
- else:
- return "[%s, ...]" % _print_value(val[0])
- return str(val)
-
- def _wrapper(self, *args, **kwargs):
- """
- Wrapper for function or method, that does the logging.
- """
- ftype = "method" if inspect.ismethod(func) else "function"
- parent = ( func.im_class.__name__ + "."
- if inspect.ismethod(func) else "")
-
- _logger().debug("entering %s %s%s with args=(%s)",
- ftype, parent, func.__name__,
- ", ".join(chain(
- (_print_value(a) for a in args),
- ( "%s=%s"%(k, _print_value(v))
- for k, v in kwargs.items()))))
- result = func(self, *args, **kwargs)
- _logger().debug("exiting %s %s%s", ftype, parent, func.__name__)
- return result
-
- return _wrapper
-
def _needs_database(method):
"""
Decorator for YumWorker job handlers, that need to access the yum database.
It ensures, that database is initialized and locks it in case, that
no session is active.
"""
+ logged = trace_function(method)
def _wrapper(self, *args, **kwargs):
"""
Wrapper for the job handler method.
"""
+ created_session = False
self._init_database() #pylint: disable=W0212
if self._session_level == 0: #pylint: disable=W0212
+ self._session_level = 1 #pylint: disable=W0212
+ created_session = True
self._lock_database() #pylint: disable=W0212
try:
- _logger().debug("calling job handler %s with args=(%s)",
+ LOG.debug("calling job handler %s with args=(%s)",
method.__name__,
", ".join(chain(
(str(a) for a in args),
("%s=%s"%(k, str(v)) for k, v in kwargs.items()))))
- result = method(self, *args, **kwargs)
- _logger().debug("job handler %s finished", method.__name__)
+ result = logged(self, *args, **kwargs)
+ LOG.debug("job handler %s finished", method.__name__)
return result
- except:
- _logger().debug("job handler %s terminated with exception: %s",
- method.__name__, traceback.format_exc())
- raise
finally:
- if self._session_level == 0: #pylint: disable=W0212
+ if created_session is True: #pylint: disable=W0212
+ self._session_level = 0 #pylint: disable=W0212
self._unlock_database() #pylint: disable=W0212
return _wrapper
@@ -192,95 +200,110 @@ class YumWorker(Process):
"""
The main process, that works with YUM API. It has two queues, one
for input jobs and second for results.
-
+
Jobs are dispatched by their class names to particular handler method.
+
+ It spawns a second thread for managing asynchronous jobs and queue
+ of incoming jobs. It's an instance of JobManager.
"""
def __init__(self,
queue_in,
queue_out,
- yum_args=None,
yum_kwargs=None,
logging_config=None):
- Process.__init__(self)
- self._queue_in = queue_in
- self._queue_out = queue_out
+ Process.__init__(self, name="YumWorker")
+ self._jobmgr = JobManager(queue_in, queue_out)
self._session_level = 0
self._session_ended = False
- if yum_args is None:
- yum_args = tuple()
if yum_kwargs is None:
yum_kwargs = {}
- self._yum_args = (yum_args, yum_kwargs)
+ self._yum_kwargs = yum_kwargs
self._yum_base = None
self._pkg_cache = None
+ # contains (repoid, time_stamp_of_config_file)
+ # plus (/repos/dir, ...) for each repo config directory
+ self._repodir_mtimes = {}
self._logging_config = logging_config
# *************************************************************************
# Private methods
# *************************************************************************
- @_trace_function
+ @trace_function
def _init_database(self):
"""
Initializes yum base object, when it does no exists.
And updates the cache (when out of date).
"""
if self._yum_base is None:
- _logger().info("creating YumBase with args=(%s)",
- ", ".join(chain(
- (str(a) for a in self._yum_args[0]),
- ( "%s=%s"%(k, str(v))
- for k, v in self._yum_args[1].items()))))
- self._yum_base = yum.YumBase(
- *self._yum_args[0], **self._yum_args[1])
-
- @_trace_function
+ LOG.info("creating YumBase with kwargs=(%s)",
+ ", ".join(( "%s=%s"%(k, str(v))
+ for k, v in self._yum_kwargs.items())))
+ self._yum_base = yum.YumBase(**self._yum_kwargs)
+
+ @trace_function
def _free_database(self):
"""
Release the yum base object to safe memory.
"""
- _logger().info("freing database")
+ LOG.info("freing database")
self._pkg_cache.clear()
self._yum_base = None
- @_trace_function
+ @trace_function
def _lock_database(self):
"""
Only one process is allowed to work with package database at given time.
That's why we lock it.
-
+
Try to lock it in loop, until success.
"""
while True:
try:
- _logger().info("trying to lock database - session level %d",
+ LOG.info("trying to lock database - session level %d",
self._session_level)
self._yum_base.doLock()
- _logger().info("successfully locked up")
+ LOG.info("successfully locked up")
break
except yum.Errors.LockError as exc:
- _logger().warn("failed to lock")
+ LOG.warn("failed to lock")
if exc.errno in (errno.EPERM, errno.EACCES, errno.ENOSPC):
- _logger().error("can't create lock file")
+ LOG.error("can't create lock file")
raise errors.DatabaseLockError("Can't create lock file.")
- _logger().info("trying to lock again after %.1f seconds",
+ LOG.info("trying to lock again after %.1f seconds",
LOCK_WAIT_INTERVAL)
time.sleep(LOCK_WAIT_INTERVAL)
- @_trace_function
+ @trace_function
def _unlock_database(self):
"""
The opposite to _lock_database() method.
"""
if self._yum_base is not None:
- _logger().info("unlocking database")
+ LOG.info("unlocking database")
self._yum_base.closeRpmDB()
self._yum_base.doUnlock()
- @_trace_function
+ @trace_function
+ def _get_job(self):
+ """
+ Get job from JobManager thread.
+ If no job comes for long time, free database to save memory.
+ """
+ while True:
+ if self._session_ended and self._session_level == 0:
+ try:
+ return self._jobmgr.get_job(timeout=FREE_DATABASE_TIMEOUT)
+ except TQueue.Empty:
+ self._free_database()
+ self._session_ended = False
+ else:
+ return self._jobmgr.get_job()
+
+ @trace_function
def _transform_packages(self, packages,
cache_packages=True,
flush_cache=True):
@@ -293,17 +316,17 @@ class YumWorker(Process):
packages; makes sense only with cachee_packages=True
"""
if cache_packages is True and flush_cache is True:
- _logger().debug("flushing package cache")
+ LOG.debug("flushing package cache")
self._pkg_cache.clear()
res = []
for orig in packages:
pkg = packageinfo.make_package_from_db(orig)
if cache_packages is True:
- self._pkg_cache[pkg.pkgid] = orig
+ self._pkg_cache[pkg.objid] = orig
res.append(pkg)
return res
- @_trace_function
+ @trace_function
def _cache_packages(self, packages, flush_cache=True, transform=False):
"""
Store packages in cache and return them.
@@ -315,13 +338,13 @@ class YumWorker(Process):
if transform is True:
return self._transform_packages(packages, flush_cache=flush_cache)
if flush_cache is True:
- _logger().debug("flushing package cache")
+ LOG.debug("flushing package cache")
self._pkg_cache.clear()
for pkg in packages:
self._pkg_cache[id(pkg)] = pkg
return packages
- @_trace_function
+ @trace_function
def _lookup_package(self, pkg):
"""
Lookup the original package in cache.
@@ -330,14 +353,14 @@ class YumWorker(Process):
"""
if not isinstance(pkg, packageinfo.PackageInfo):
raise TypeError("pkg must be instance of PackageInfo")
- _logger().debug("looking up yum package %s with id=%d",
- pkg, pkg.pkgid)
+ LOG.debug("looking up yum package %s with id=%d",
+ pkg, pkg.objid)
try:
- result = self._pkg_cache[pkg.pkgid]
- _logger().debug("lookup successful")
+ result = self._pkg_cache[pkg.objid]
+ LOG.debug("lookup successful")
except KeyError:
- _logger().warn("lookup of package %s with id=%d failed, trying"
- " to query database", pkg, pkg.pkgid)
+ LOG.warn("lookup of package %s with id=%d failed, trying"
+ " to query database", pkg, pkg.objid)
result = self._handle_filter_packages(
'installed' if pkg.installed else 'available',
allow_duplicates=False,
@@ -345,13 +368,70 @@ class YumWorker(Process):
transform=False,
**pkg.key_props)
if len(result) < 1:
- _logger().warn("package %s not found", pkg)
+ LOG.warn("package %s not found", pkg)
raise errors.PackageNotFound(
"package %s could not be found" % pkg)
result = result[0]
return result
- @_trace_function
+ @trace_function
+ def _clear_repository_cache(self):
+ """
+ Clears the repository cache and their configuration directory
+ last modification times.
+ """
+ if self._yum_base is not None:
+ for repoid in self._yum_base.repos.repos.keys():
+ self._yum_base.repos.delete(repoid)
+ del self._yum_base.repos
+ del self._yum_base.pkgSack
+ self._repodir_mtimes.clear()
+
+ @trace_function
+ def _check_repository_configs(self):
+ """
+ Checks whether repository information is up to date with configuration
+ files by comparing timestamps. If not, repository cache will be
+ released.
+ """
+ dirty = False
+ if self._repodir_mtimes:
+ for repodir in self._yum_base.conf.reposdir:
+ if ( os.path.exists(repodir)
+ and ( not repodir in self._repodir_mtimes
+ or ( os.stat(repodir).st_mtime
+ > self._repodir_mtimes[repodir]))):
+ LOG.info("repository config dir %s changed", repodir)
+ dirty = True
+ break
+ if not dirty:
+ for repo in self._yum_base.repos.repos.values():
+ filename = repo.repofile
+ if ( not os.path.exists(filename)
+ or ( int(os.stat(filename).st_mtime)
+ > repo.repo_config_age)):
+ LOG.info('config file of repository "%s" changed',
+ repo.id)
+ dirty = True
+ break
+ if dirty is True:
+ LOG.info("repository cache is dirty, cleaning up ...")
+ self._clear_repository_cache()
+ self._yum_base.getReposFromConfig()
+ if dirty is True or not self._repodir_mtimes:
+ self._update_repodir_mtimes()
+
+ @trace_function
+ def _update_repodir_mtimes(self):
+ """
+ Updates the last modification times of repo configuration directories.
+ """
+ assert self._yum_base is not None
+ for repodir in self._yum_base.conf.reposdir:
+ if os.path.exists(repodir):
+ self._repodir_mtimes[repodir] = os.stat(repodir).st_mtime
+
+ @trace_function
def _do_work(self, job):
"""
Dispatcher of incoming jobs. Job is passed to the right handler
@@ -369,103 +449,176 @@ class YumWorker(Process):
jobs.YumUpdatePackage : self._handle_update_package,
jobs.YumBeginSession : self._handle_begin_session,
jobs.YumEndSession : self._handle_end_session,
- jobs.YumCheckPackage : self._handle_check_package
+ jobs.YumCheckPackage : self._handle_check_package,
+ jobs.YumInstallPackageFromURI : \
+ self._handle_install_package_from_uri,
+ jobs.YumGetRepositoryList : \
+ self._handle_get_repository_list,
+ jobs.YumFilterRepositories : self._handle_filter_repositories,
+ jobs.YumSetRepositoryEnabled : \
+ self._handle_set_repository_enabled
}[job.__class__]
- _logger().info("processing job %s(id=%d)",
+ LOG.info("processing job %s(id=%d)",
job.__class__.__name__, job.jobid)
except KeyError:
- _logger().info("No handler for job \"%s\"", job.__class__.__name__)
+ LOG.error("No handler for job \"%s\"", job.__class__.__name__)
raise errors.UnknownJob("No handler for job \"%s\"." %
job.__class__.__name__)
return handler(**job.job_kwargs)
- @_trace_function
+ @trace_function
def _run_transaction(self, name):
"""
Builds and runs the yum transaction and checks for errors.
@param name of transaction used only in error description on failure
"""
- _logger().info("building transaction %s", name)
+ LOG.info("building transaction %s", name)
(code, msgs) = self._yum_base.buildTransaction()
if code == 1:
- _logger().error("building transaction %s failed: %s",
+ LOG.error("building transaction %s failed: %s",
name, "\n".join(msgs))
raise errors.TransactionBuildFailed(
"Failed to build \"%s\" transaction: %s" % (
name, "\n".join(msgs)))
- _logger().info("processing transaction %s", name)
+ LOG.info("processing transaction %s", name)
self._yum_base.processTransaction()
self._yum_base.closeRpmDB()
- @_trace_function
+ @trace_function
+ def _main_loop(self):
+ """
+ This is a main loop called from run(). Jobs are handled here.
+ It accepts a job from input queue, handles it,
+ sends the result to output queue and marks the job as done.
+
+ It is terminated, when None is received from input queue.
+ """
+ while True:
+ job = self._get_job()
+ if job is not None: # not a terminate command
+ result = jobs.YumJob.RESULT_SUCCESS
+ try:
+ data = self._do_work(job)
+ except Exception: #pylint: disable=W0703
+ result = jobs.YumJob.RESULT_ERROR
+ # (type, value, traceback)
+ data = sys.exc_info()
+ # traceback is not pickable - replace it with formatted
+ # text
+ data = (data[0], data[1], traceback.format_tb(data[2]))
+ LOG.exception("job %s failed", job)
+ self._jobmgr.finish_job(job, result, data)
+ if job is None:
+ LOG.info("waiting for %s to finish", self._jobmgr.name)
+ self._jobmgr.join()
+ break
+
+ @trace_function
def _handle_begin_session(self):
"""
Handler for session begin job.
"""
self._session_level += 1
- _logger().info("beginning session level %s", self._session_level)
+ LOG.info("beginning session level %s", self._session_level)
if self._session_level == 1:
self._init_database()
self._lock_database()
- @_trace_function
+ @trace_function
def _handle_end_session(self):
"""
Handler for session end job.
"""
- _logger().info("ending session level %d", self._session_level)
- self._session_level -= 1
+ LOG.info("ending session level %d", self._session_level)
+ self._session_level = max(self._session_level - 1, 0)
if self._session_level == 0:
self._unlock_database()
self._session_ended = True
@_needs_database
def _handle_get_package_list(self, kind, allow_duplicates, sort,
- transform=True):
+ include_repos=None, exclude_repos=None, transform=True):
"""
Handler for listing packages job.
@param transform says, whether to return just a package abstractions
or original ones
@return [pkg1, pkg2, ...]
"""
- pkglist = self._yum_base.doPackageLists(kind, showdups=allow_duplicates)
- if kind == 'all':
- result = pkglist.available + pkglist.installed
+ if kind == 'avail_notinst':
+ what = 'available'
+ elif kind == 'available':
+ what = 'all'
+ elif kind == 'avail_reinst':
+ what = 'all'
else:
- result = getattr(pkglist, kind)
+ what = kind
+ with RepoFilterSetter(self._yum_base, include_repos, exclude_repos):
+ LOG.debug("calling YumBase.doPackageLists(%s, showdups=%s)",
+ what, allow_duplicates)
+ pkglist = self._yum_base.doPackageLists(what,
+ showdups=allow_duplicates)
+ if kind == 'all':
+ result = pkglist.available + pkglist.installed
+ elif kind == 'available':
+ result = pkglist.available + pkglist.reinstall_available
+ elif kind == 'avail_reinst':
+ result = pkglist.reinstall_available
+ else: # get installed or available
+ result = getattr(pkglist, what)
if sort is True:
result.sort()
- _logger().debug("returning %s packages", len(result))
+ LOG.debug("returning %s packages", len(result))
return self._cache_packages(result, transform=transform)
@_needs_database
def _handle_filter_packages(self, kind, allow_duplicates, sort,
+ include_repos=None, exclude_repos=None,
transform=True, **filters):
"""
Handler for filtering packages job.
@return [pkg1, pkg2, ...]
"""
pkglist = self._handle_get_package_list(kind, allow_duplicates, False,
+ include_repos=include_repos, exclude_repos=exclude_repos,
transform=False)
matches = _get_package_filter_function(filters)
result = [p for p in pkglist if matches(p)]
if sort is True:
result.sort()
- _logger().debug("%d packages matching", len(result))
+ LOG.debug("%d packages matching", len(result))
if transform is True:
# caching has been already done by _handle_get_package_list()
result = self._transform_packages(result, cache_packages=False)
return result
@_needs_database
- def _handle_install_package(self, pkg):
+ def _handle_install_package(self, pkg, force=False):
"""
Handler for package installation job.
@return installed package instance
"""
- pkg_desired = self._lookup_package(pkg)
- self._yum_base.install(pkg_desired)
- self._run_transaction("install")
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages(
+ 'available' if force else 'avail_notinst',
+ allow_duplicates=False, sort=True,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ elif len(pkgs) > 1:
+ LOG.warn('multiple packages matches nevra "%s": [%s]',
+ pkg, ", ".join(p.nevra for p in pkgs))
+ pkg_desired = pkgs[-1]
+ else:
+ pkg_desired = self._lookup_package(pkg)
+ if isinstance(pkg_desired, yum.rpmsack.RPMInstalledPackage):
+ if force is False:
+ raise errors.PackageAlreadyInstalled(pkg)
+ action = "reinstall"
+ else:
+ action = "install"
+ getattr(self._yum_base, action)(pkg_desired)
+ self._run_transaction(action)
installed = self._handle_filter_packages("installed", False, False,
nevra=util.pkg2nevra(pkg_desired, with_epoch="ALWAYS"))
if len(installed) < 1:
@@ -478,7 +631,16 @@ class YumWorker(Process):
"""
Handler for package removal job.
"""
- pkg = self._lookup_package(pkg)
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg = pkgs[-1]
+ else:
+ pkg = self._lookup_package(pkg)
self._yum_base.remove(pkg)
self._run_transaction("remove")
@@ -488,7 +650,16 @@ class YumWorker(Process):
Handler for specific package update job.
@return package corresponding to pkg after update
"""
- pkg_desired = self._lookup_package(pkg)
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('available',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg_desired = pkgs[-1]
+ else:
+ pkg_desired = self._lookup_package(pkg)
self._yum_base.update(update_to=True,
name=pkg_desired.name,
epoch=pkg_desired.epoch,
@@ -504,12 +675,24 @@ class YumWorker(Process):
return installed[0]
@_needs_database
- def _handle_update_package(self, pkg, to_epoch, to_version, to_release):
+ def _handle_update_package(self, pkg, to_epoch, to_version, to_release,
+ _force=False):
"""
Handler for package update job.
@return updated package instance
"""
- pkg = self._lookup_package(pkg)
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg = pkgs[-1]
+ else:
+ pkg = self._lookup_package(pkg)
+ if not isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ raise errors.PackageNotInstalled(pkg)
kwargs = { "name" : pkg.name, "arch" : pkg.arch }
if any(v is not None for v in (to_epoch, to_version, to_release)):
kwargs["update_to"] = True
@@ -535,11 +718,140 @@ class YumWorker(Process):
"""
@return PackageFile instance for requested package
"""
- pkg = self._lookup_package(pkg)
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg = pkgs[-1]
+ else:
+ pkg = self._lookup_package(pkg)
+ if not isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ raise errors.PackageNotInstalled(pkg)
vpkg = yum.packages._RPMVerifyPackage(pkg, pkg.hdr.fiFromHeader(),
packagecheck.pkg_checksum_type(pkg), [], True)
return packagecheck.make_package_check_from_db(vpkg)
+ @_needs_database
+ def _handle_install_package_from_uri(self, uri,
+ update_only=False, force=False):
+ """
+ @return installed PackageInfo instance
+ """
+ try:
+ pkg = yum.packages.YumUrlPackage(self,
+ ts=self._yum_base.rpmdb.readOnlyTS(), url=uri,
+ ua=default_grabber)
+ except yum.Errors.MiscError as exc:
+ raise errors.PackageOpenError(uri, str(exc))
+ installed = self._handle_filter_packages("installed", False, False,
+ nevra=util.pkg2nevra(pkg, with_epoch="ALWAYS"))
+ if installed and force is False:
+ raise errors.PackageAlreadyInstalled(pkg)
+ kwargs = { 'po' : pkg }
+ if installed:
+ action = 'reinstallLocal'
+ else:
+ action = 'installLocal'
+ kwargs = { 'updateonly' : update_only }
+ getattr(self._yum_base, action)(uri, **kwargs)
+ self._run_transaction('installLocal')
+ installed = self._handle_filter_packages("installed", False, False,
+ nevra=util.pkg2nevra(pkg, with_epoch="ALWAYS"))
+ if len(installed) < 1:
+ raise errors.TransactionExecutionFailed(
+ "Failed to install desired package %s." % pkg)
+ return installed[0]
+
+ @_needs_database
+ def _handle_get_repository_list(self, kind, transform=True):
+ """
+ @return list of yumdb.Repository instances
+ """
+ self._check_repository_configs()
+ if kind == 'enabled':
+ repos = sorted(self._yum_base.repos.listEnabled())
+ else:
+ repos = self._yum_base.repos.repos.values()
+ if kind == 'disabled':
+ repos = [repo for repo in repos if not repo.enabled]
+ repos.sort()
+ if transform:
+ repos = [repository.make_repository_from_db(r) for r in repos]
+ LOG.debug("returning %d repositories from %s",
+ len(repos), kind)
+ return repos
+
+ @_needs_database
+ def _handle_filter_repositories(self, kind, **filters):
+ """
+ @return list of yumdb.Repository instances -- filtered
+ """
+ filters = dict((k, v) for k, v in filters.items() if v is not None)
+ if 'repoid' in filters:
+ self._check_repository_configs()
+ try:
+ repo = repository.make_repository_from_db(
+ self._yum_base.repos.getRepo(filters["repoid"]))
+ if ( (kind == "enabled" and not repo.enabled)
+ or (kind == "disabled" and repo.enabled)):
+ LOG.warn(
+ 'no such repository with id="%s"matching filters',
+ filters['repoid'])
+ return []
+ LOG.debug(
+ "exactly one repository matching filters found")
+ return [repo]
+ except (KeyError, yum.Errors.RepoError):
+ LOG.warn('repository with id="%s" could not be found',
+ filters['repoid'])
+ raise errors.RepositoryNotFound(filters['repoid'])
+ repos = self._handle_get_repository_list(kind, transform=False)
+ result = []
+ for repo in repos:
+ # do the filtering and safe transformed repo into result
+ for prop, value in filters.items():
+ if repository.get_prop_from_yum_repo(repo, prop) != value:
+ # did not pass the filter
+ break
+ else: # all properties passed
+ result.append(repository.make_repository_from_db(repo))
+ LOG.debug("found %d repositories matching", len(result))
+ return result
+
+ @_needs_database
+ def _handle_set_repository_enabled(self, repo, enable):
+ """
+ @return previous enabled state
+ """
+ try:
+ repo = self._yum_base.repos.getRepo(repo.repoid)
+ except (KeyError, yum.Errors.RepoError):
+ raise errors.RepositoryNotFound(repo.repoid)
+ res = repo.enabled
+ try:
+ if enable ^ res:
+ if enable is True:
+ LOG.info("enabling repository %s" % repo)
+ repo.enable()
+ else:
+ LOG.info("disabling repository %s" % repo)
+ repo.disable()
+ try:
+ yum.config.writeRawRepoFile(repo, only=["enabled"])
+ except Exception as exc:
+ raise errors.RepositoryChangeError(
+ 'failed to modify repository "%s": %s - %s' % (
+ repo, exc.__class__.__name__, str(exc)))
+ else:
+ LOG.info("no change for repo %s", repo)
+ except yum.Errors.RepoError as exc:
+ raise errors.RepositoryChangeError(
+ 'failed to modify repository "%s": %s' % (repo, str(exc)))
+ return res
+
# *************************************************************************
# Public properties
# *************************************************************************
@@ -548,58 +860,32 @@ class YumWorker(Process):
"""
@return input queue for jobs
"""
- return self._queue_in
+ return self._jobmgr.queue_in
@property
def downlink(self):
"""
@return output queue for job results
"""
- return self._queue_out
+ return self._jobmgr.queue_out
# *************************************************************************
# Public methods
# *************************************************************************
def run(self):
"""
- Main loop of process. It accepts a job from input queue, handles it,
- sends the result to output queue and marks the job as done.
-
- It is terminated, when None is received from input queue.
+ Thread's entry point. After initial setup it calls _main_loop().
"""
if self._logging_config is not None:
- try:
- logging.config.dictConfig(self._logging_config)
- except Exception: #pylint: disable=W0703
- # logging is not set up but client expects us to work
- pass
- _logger().info("starting %s main loop", self.__class__.__name__)
+ setup_logging(self._logging_config)
+ global LOG
+ LOG = logging.getLogger(__name__)
+ LOG.info("running as pid=%d", self.pid)
+ self._jobmgr.start()
+ LOG.info("started %s as thread %s",
+ self._jobmgr.name, self._jobmgr.ident)
self._pkg_cache = weakref.WeakValueDictionary()
- while True:
- if self._session_ended and self._session_level == 0:
- try:
- job = self._queue_in.get(True, FREE_DATABASE_TIMEOUT)
- except TQueue.Empty:
- self._free_database()
- self._session_ended = False
- continue
- else:
- job = self._queue_in.get()
- if job is not None: # not a terminate command
- try:
- result = self._do_work(job)
- except Exception: #pylint: disable=W0703
- # (type, value, traceback)
- result = sys.exc_info()
- # traceback is not pickable - replace it with formatted
- # text
- result = ( result[0], result[1]
- , traceback.format_tb(result[2]))
- _logger().error("job %s(id=%d) failed: %s",
- job.__class__.__name__, job.jobid,
- traceback.format_exc())
- self._queue_out.put((job.jobid, result))
- self._queue_in.task_done()
- if job is None:
- break
+
+ self._main_loop()
+ LOG.info("terminating")
diff --git a/src/software/openlmi/software/yumdb/repository.py b/src/software/openlmi/software/yumdb/repository.py
new file mode 100644
index 0000000..2863dac
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/repository.py
@@ -0,0 +1,190 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding an abstraction for YUM repository.
+"""
+
+from datetime import datetime
+import yum
+
+# maps names of Repository properties to their corresponding property
+# names in YumRepository object
+PROPERTY_NAME_MAP = {
+ "repoid" : "id",
+ "base_urls" : "baseurl",
+ "config_file" : "repofile",
+ "cost" : "cost",
+ "enabled" : "enabled",
+ "gpg_check" : "gpgcheck",
+ "last_edit" : "repo_config_age",
+ "mirror_list" : "mirrorlist",
+ "mirror_urls" : "mirrorurls",
+ "name" : "name",
+ "pkg_dir" : "pkgdir",
+ "ready" : "ready",
+ "repo_gpg_check" : "repo_gpgcheck",
+ "timeout" : "timeout"
+}
+
+def get_prop_from_yum_repo(repo, prop_name):
+ """
+ @param prop_name is one Repository properties
+ @return value of property from object of YumRepository
+ """
+ if not isinstance(repo, yum.yumRepo.YumRepository):
+ raise TypeError("repo must be in instance of yum.yumRepo.YumRepository")
+ if prop_name in PROPERTY_NAME_MAP:
+ val = getattr(repo, PROPERTY_NAME_MAP[prop_name])
+ if prop_name == "last_edit":
+ val = datetime.fromtimestamp(val)
+ elif prop_name == "mirror_urls" and not repo.mirrorlist:
+ val = None
+ elif prop_name == "ready":
+ val = val()
+ elif prop_name in {"arch", "basearch", "releasever"}:
+ val = repo.yumvar[prop_name]
+ elif prop_name in {"revision", "last_update"}:
+ if repo.enabled and repo.repoXML:
+ md = repo.repoXML
+ if prop_name == "last_update":
+ val = datetime.fromtimestamp(md.timestamp)
+ elif prop_name == "revision":
+ val = long(md.revision)
+ else:
+ val = getattr(repo.repoXML, prop_name)
+ else:
+ val = None
+ elif prop_name == "pkg_count":
+ # this needs populated sack: ydb.repos.populateSack(repo.id)
+ val = len(repo.sack)
+ else:
+ raise ValueError('Unknown repository property: "%s"' % prop_name)
+ return val
+
+class Repository(object):
+ """
+ Container for repository metadata. It represents yum repository.
+ It's supposed to be passed from YumWorker to YumDB client and
+ vice-versa.
+ """
+ __slots__ = (
+ "objid", # [int] id of python object on server process
+ "repoid", # [string] repository id name
+ # (name of config file)
+
+ "arch", # [string] architecture of packages
+ "basearch", # [string] base system architecture
+ "base_urls", # [list] base urls as strings
+ #"cache",
+ #"cache_dir",
+ "name", # [string] repository descriptive name
+ "config_file", # [string] file path to corresponding
+ # config file
+ "cost", # [int] cost of repository
+ "enabled", # [boolean] whether repo is enabled
+ "gpg_check", # [boolean] whether to check gpg signature
+ #"metadata_expire", # how long are metadata considered valid
+ "last_edit", # datetime of last config modification
+ "last_update", # datetime of last change of repository
+ # on server
+ "mirror_list", # url of mirrorlist, or None
+ "mirror_urls", # list of urls obtained from mirrorlist or None
+ #"persist_dir", #
+ #"pkg_count", # number of packages in directory
+ "pkg_dir", # directory with packages
+ #"proxy", # boolean saying whether this is a proxy
+ "ready", # boolean saying, whether it's ready for use
+ "releasever", # version of targeted distribution
+ "repo_gpg_check", # [boolean] whether to check gpg
+ # signarues of data
+ "revision",
+ "timeout", # timeout for requests
+ )
+
+ def __init__(self, objid, repoid, arch, basearch, base_urls,
+ config_file, cost, enabled, gpg_check, last_edit, last_update,
+ name, pkg_dir, ready, releasever, repo_gpg_check, revision,
+ timeout, mirror_list=None, mirror_urls=None):
+ for arg in ('last_edit', 'last_update'):
+ if ( locals()[arg] is not None
+ and not isinstance(locals()[arg], datetime)):
+ raise TypeError("%s must be an instance of datetime" % arg)
+ if not isinstance(timeout, float):
+ raise TypeError("timeout must be a float")
+ for arg in ('cost', 'revision'):
+ if ( locals()[arg] is not None
+ and not isinstance(locals()[arg], (int, long))):
+ raise TypeError("%s must be an integer" % arg)
+ self.objid = objid
+ self.repoid = repoid
+ self.arch = arch
+ self.basearch = basearch
+ self.base_urls = list(base_urls)
+ self.config_file = config_file
+ self.cost = cost
+ self.enabled = bool(enabled)
+ self.gpg_check = bool(gpg_check)
+ self.last_edit = last_edit
+ self.last_update = last_update
+ self.mirror_list = "" if not mirror_list else mirror_list
+ self.mirror_urls = [] if not mirror_urls else list(mirror_urls)
+ self.name = name
+ #self.pkg_count = pkg_count
+ self.pkg_dir = pkg_dir
+ self.ready = bool(ready)
+ self.releasever = releasever
+ self.repo_gpg_check = bool(repo_gpg_check)
+ self.revision = revision
+ self.timeout = timeout
+
+ def __str__(self):
+ return self.repoid
+
+ def __getstate__(self):
+ """
+ Used for serialization with pickle.
+ @return container content that will be serialized
+ """
+ return dict((k, getattr(self, k)) for k in self.__slots__)
+
+ def __setstate__(self, state):
+ """
+ Used for deserialization with pickle.
+ Restores the object from serialized form.
+ @param state is an object created by __setstate__() method
+ """
+ for k, value in state.items():
+ setattr(self, k, value)
+
+def make_repository_from_db(repo):
+ """
+ Create instance of Repository from instance of yum.yumRepo.YumRepository
+ @return instance of Repository
+ """
+ if not isinstance(repo, yum.yumRepo.YumRepository):
+ raise TypeError("repo must be in instance of yum.yumRepo.YumRepository")
+ metadata = dict(
+ (prop_name, get_prop_from_yum_repo(repo, prop_name))
+ for prop_name in Repository.__slots__[1:])
+ res = Repository(id(repo), **metadata)
+ return res
+
diff --git a/src/software/openlmi/software/yumdb/util.py b/src/software/openlmi/software/yumdb/util.py
new file mode 100644
index 0000000..f2af151
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/util.py
@@ -0,0 +1,97 @@
+from itertools import chain
+import inspect
+import logging
+import os
+
+from openlmi.software.yumdb import errors
+
+class DispatchingFormatter:
+ def __init__(self, formatters, default):
+ for k, formatter in formatters.items():
+ if isinstance(formatter, basestring):
+ formatters[k] = logging.Formatter(formatter)
+ self._formatters = formatters
+ if isinstance(default, basestring):
+ default = logging.Formatter(default)
+ self._default_formatter = default
+
+ def format(self, record):
+ formatter = self._formatters.get(record.name, self._default_formatter)
+ return formatter.format(record)
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def trace_function(func):
+ """
+ Decorator for logging entries and exits of function or method.
+ """
+ if not inspect.ismethod(func) and not inspect.isfunction(func):
+ raise TypeError("func must be a function")
+
+ def _print_value(val):
+ """
+ Used here for printing function arguments. Shortens the output
+ string, if that would be too long.
+ """
+ if isinstance(val, list):
+ if len(val) < 2:
+ return str(val)
+ else:
+ return "[%s, ...]" % _print_value(val[0])
+ return str(val)
+
+ logger = logging.getLogger(__name__+'.trace_function')
+ module = func.__module__.split('.')[-1]
+ lineno = inspect.currentframe().f_back.f_lineno
+
+ def _wrapper(self, *args, **kwargs):
+ """
+ Wrapper for function or method, that does the logging.
+ """
+ if logger.isEnabledFor(logging.DEBUG):
+ frm = inspect.currentframe()
+ logargs = {
+ "caller_file" : os.path.basename(os.path.splitext(
+ frm.f_back.f_code.co_filename)[0]),
+ "caller_lineno" : frm.f_back.f_lineno,
+ "module" : module,
+ "func" : func.__name__,
+ "lineno" : lineno,
+ "action" : "entering",
+ "args" : ", ".join(chain(
+ (_print_value(a) for a in args),
+ ( "%s=%s"%(k, _print_value(v))
+ for k, v in kwargs.items())))
+ }
+
+ if not logargs["args"]:
+ logargs["args"] = ""
+ else:
+ logargs["args"] = " with args=(%s)" % logargs["args"]
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d%(args)s" , logargs)
+ try:
+ result = func(self, *args, **kwargs)
+ if logger.isEnabledFor(logging.DEBUG):
+ logargs["action"] = "exiting"
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d", logargs)
+ except Exception as exc:
+ logargs['action'] = 'exiting'
+ logargs['error'] = str(exc)
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d with error: %(error)s",
+ logargs)
+ raise
+ return result
+
+ return _wrapper
+
+def setup_logging(config):
+ try:
+ logging.config.dictConfig(config)
+ except Exception: #pylint: disable=W0703
+ # logging is not set up but client expects us to work
+ pass
+
diff --git a/src/software/setup.py b/src/software/setup.py
index a044ac0..bede36f 100644
--- a/src/software/setup.py
+++ b/src/software/setup.py
@@ -5,7 +5,7 @@ setup(
author='Michal Minar',
author_email='miminar@redhat.com',
url='https://fedorahosted.org/openlmi/',
- version='0.6',
+ version='0.9',
namespace_packages=['openlmi'],
packages=[
'openlmi.software',
diff --git a/src/software/test/common.py b/src/software/test/base.py
index fbcb5d4..2fe1173 100644
--- a/src/software/test/common.py
+++ b/src/software/test/base.py
@@ -21,61 +21,16 @@
Common utilities and base class for all software tests.
"""
+import itertools
import os
import pywbem
-import re
import tempfile
import unittest
-from subprocess import call, check_output
+from subprocess import check_output
+import repository
import rpmcache
-RE_NEVRA = re.compile(r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<ver>[^-]+)'
- r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
-
-def remove_pkg(pkg, *args):
- """
- Remove package with rpm command.
- @param pkg is either instance of Package or package name
- @param args is a list of parameters for rpm command
- """
- if isinstance(pkg, rpmcache.Package):
- pkg = pkg.name
- call(["rpm", "--quiet"] + list(args) + ["-e", pkg])
-
-def install_pkg(pkg, newer=True, repolist=[]):
- """
- Install a specific package.
- @param pkg is either package name or instance of Package
- In latter case, a specific version is installed.
- @param repolist is a list of repositories, that should be
- used for downloading, if using yum
- when empty, all enabled repositories are used
- """
- if isinstance(pkg, rpmcache.Package):
- try:
- rpm_name = rpmcache.get_rpm_name(pkg, newer=newer)
- call(["rpm", "--quiet", "-i", rpm_name])
- return
- except rpmcache.MissingRPM:
- pass
- pkg = pkg.name
- rpmcache.run_yum('-q', '-y', 'install', pkg, repolist=repolist)
-
-def is_installed(pkg, newer=True):
- """
- Check, whether package is installed.
- Accepts the same parameters as install_pkg.
- @see install_pkg
- """
- return rpmcache.is_installed(pkg, newer)
-
-def verify_pkg(name):
- """
- @return output of command rpm, with verification output for package
- """
- return call(["rpm", "--quiet", "-Va", name]) == 0
-
def mark_dangerous(method):
"""
Decorator for methods of unittest.TestCase subclasses, that
@@ -86,21 +41,17 @@ def mark_dangerous(method):
else:
return unittest.skip("This test is marked as dangerous.")(method)
-def is_config_file(pkg, file_path):
- """
- @return True, if file_path is a configuration file of package pkg.
+def mark_tedious(method):
"""
- cmd = ['rpm', '-qc', pkg.name]
- out = check_output(cmd)
- return file_path in set(out.splitlines()) #pylint: disable=E1103
-
-def is_doc_file(pkg, file_path):
- """
- @return True, if file_path is a documentation file of package pkg.
+ Decorator for methods of unittest.TestCase subclasses, that
+ skips tedious tests. Those running for very long time and usually
+ need a lot of memory. Environment variable "LMI_RUN_TEDIOUS" can
+ allow them
"""
- cmd = ['rpm', '-qd', pkg.name]
- out = check_output(cmd)
- return file_path in set(out.splitlines()) #pylint: disable=E1103
+ if os.environ.get('LMI_RUN_TEDIOUS', '0') == '1':
+ return method
+ else:
+ return unittest.skip("This test is marked as tedious.")(method)
def get_pkg_files(pkg):
"""
@@ -124,9 +75,9 @@ def get_pkg_files(pkg):
elif len(symlinks) == 0 and os.path.islink(fpath):
symlinks.add(fpath)
elif not os.path.islink(fpath) and os.path.isfile(fpath):
- if len(configs) == 0 and is_config_file(pkg, fpath):
+ if len(configs) == 0 and rpmcache.has_pkg_config_file(pkg, fpath):
configs.add(fpath)
- elif len(docs) == 0 and is_doc_file(pkg, fpath):
+ elif len(docs) == 0 and rpmcache.has_pkg_doc_file(pkg, fpath):
docs.add(fpath)
elif len(files) == 0:
files.add(fpath)
@@ -141,6 +92,49 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
"""
CLASS_NAME = "Define in subclass"
+ KEYS = tuple()
+
+ # will be filled when first needed
+ # it's a dictionary with items (pkg_name, [file_path1, ...])
+ PKGDB_FILES = None
+ REPODB = None
+
+ @classmethod
+ def get_pkgdb_files(cls):
+ """
+ @return dictionary { pkg_name: ["file_path1, ...] }
+ """
+ if cls.PKGDB_FILES is not None:
+ return cls.PKGDB_FILES
+ SoftwareBaseTestCase.PKGDB_FILES = res = dict(
+ (pkg.name, get_pkg_files(pkg)) for pkg in itertools.chain(
+ cls.safe_pkgs, cls.dangerous_pkgs))
+ return res
+
+ @classmethod
+ def get_repodb(cls):
+ """
+ @return list of Repository instances
+ """
+ if cls.REPODB is not None:
+ return cls.REPODB
+ SoftwareBaseTestCase.REPODB = res = repository.get_repo_database()
+ return res
+
+ @classmethod
+ def needs_pkgdb(cls):
+ """subclass may override this, if it does not need PKGDB database"""
+ return True
+
+ @classmethod
+ def needs_pkgdb_files(cls):
+ """subclass may override this, if it needs PKGDB_FILES database"""
+ return False
+
+ @classmethod
+ def needs_repodb(cls):
+ """subclass may override this, if it needs REPODB database"""
+ return False
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
@@ -149,12 +143,30 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
def setUp(self):
unittest.TestCase.setUp(self)
self.objpath = pywbem.CIMInstanceName(
- namespace="root/cimv2", classname=self.CLASS_NAME)
+ namespace="root/cimv2",
+ classname=self.CLASS_NAME,
+ keybindings=pywbem.NocaseDict(
+ dict((k, None) for k in self.KEYS)))
+ if "CreationClassName" in self.KEYS:
+ self.objpath["CreationClassName"] = self.CLASS_NAME
+ if "SystemCreationClassName" in self.KEYS:
+ self.objpath["SystemCreationClassName"] = "Linux_ComputerSystem"
def install_pkg(self, pkg, newer=True, repolist=None):
+ """
+ Use this method instead of function in rpmcache in tests.
+ """
+ if repolist is None:
+ repolist = self.test_repos
+ return rpmcache.install_pkg(pkg, newer, repolist)
+
+ def ensure_pkg_installed(self, pkg, newer=True, repolist=None):
+ """
+ Use this method instead of function in rpmcache in tests.
+ """
if repolist is None:
repolist = self.test_repos
- return install_pkg(pkg, newer, repolist)
+ return rpmcache.ensure_pkg_installed(pkg, newer, repolist)
def assertIsSubclass(self, cls, base_cls): #pylint: disable=C0103
"""
@@ -170,19 +182,17 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
"root/cimv2", base_cls, cls))
def assertEqual(self, fst, snd, *args, **kwargs):
+ """
+ Modify assertEqual for instance names comparing only important
+ properties.
+ """
if ( isinstance(fst, pywbem.CIMInstanceName)
and isinstance(snd, pywbem.CIMInstanceName)
- and fst.classname == "LMI_SoftwarePackage"
and fst.classname == snd.classname
and fst.namespace == snd.namespace
- and fst.keys() == snd.keys()
- and all(fst[k] == snd[k] for k in ("Name", "SoftwareElementID",
- "SoftwareElementState", "Version"))
- and isinstance(fst["TargetOperatingSystem"], (int, long))
- and isinstance(snd["TargetOperatingSystem"], (int, long))):
+ and fst.keybindings == snd.keybindings):
return True
- return unittest.TestCase.assertEqual(
- self, fst, snd, *args, **kwargs)
+ unittest.TestCase.assertEqual(self, fst, snd, *args, **kwargs)
@classmethod
def setUpClass(cls):
@@ -193,11 +203,8 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
cls.conn = pywbem.WBEMConnection(cls.url, (cls.user, cls.password))
cls.run_dangerous = (
os.environ.get('LMI_RUN_DANGEROUS', '0') == '1')
- cls.test_repos = os.environ.get('LMI_SOFTWARE_TEST_REPOS', '')
- if not cls.test_repos:
- cls.test_repos = []
- else:
- cls.test_repos = cls.test_repos.split(',')
+ cls.test_repos = os.environ.get(
+ 'LMI_SOFTWARE_TEST_REPOS', '').split(',')
use_cache = os.environ.get('LMI_SOFTWARE_USE_CACHE', '0') == '1'
cls.cache_dir = None
if use_cache:
@@ -210,19 +217,37 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
os.makedirs(cls.cache_dir)
# rpm packages are expected to be in CWD
os.chdir(cls.cache_dir)
- cls.pkgdb = rpmcache.get_pkg_database(use_cache=use_cache)
- for pkg in cls.pkgdb:
- if not is_installed(pkg.name):
- install_pkg(pkg, repolist=cls.test_repos)
- cls.pkg_files = dict((pkg.name, get_pkg_files(pkg))
- for pkg in cls.pkgdb)
+ if not hasattr(cls, 'safe_pkgs') or not hasattr(cls, 'dangerous_pkgs'):
+ if cls.needs_pkgdb():
+ safe, dangerous = rpmcache.get_pkg_database(
+ use_cache=use_cache,
+ dangerous=cls.run_dangerous,
+ repolist=cls.test_repos,
+ cache_dir=cls.cache_dir if use_cache else None)
+ SoftwareBaseTestCase.safe_pkgs = safe
+ SoftwareBaseTestCase.dangerous_pkgs = dangerous
+ else:
+ cls.safe_pkgs = []
+ cls.dangerous_pkgs = []
+ if cls.needs_pkgdb_files() and not hasattr(cls, 'pkgdb_files'):
+ for pkg in cls.dangerous_pkgs:
+ if not rpmcache.is_pkg_installed(pkg.name):
+ rpmcache.install_pkg(pkg, repolist=cls.test_repos)
+ SoftwareBaseTestCase.pkgdb_files = cls.get_pkgdb_files()
+ if cls.needs_repodb() and not hasattr(cls, 'repodb'):
+ SoftwareBaseTestCase.repodb = cls.get_repodb()
@classmethod
def tearDownClass(cls):
+ if hasattr(cls, "repodb") and cls.repodb:
+ # set the enabled states to original values
+ for repo in cls.repodb:
+ if repository.is_repo_enabled(repo) != repo.status:
+ repository.set_repo_enabled(repo, repo.status)
if cls.run_dangerous:
- for pkg in cls.pkgdb:
- if is_installed(pkg.name):
- remove_pkg(pkg.name)
+ for pkg in cls.dangerous_pkgs:
+ if rpmcache.is_pkg_installed(pkg.name):
+ rpmcache.remove_pkg(pkg.name)
if hasattr(cls, "prev_dir"):
os.chdir(cls.prev_dir)
diff --git a/src/software/test/package.py b/src/software/test/package.py
new file mode 100644
index 0000000..9f54a8d
--- /dev/null
+++ b/src/software/test/package.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+# -*- Coding:utf-8 -*-
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details. #
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+"""
+Abstraction for RPM package for test purposes.
+"""
+
+import json
+import util
+
+class Package(object): #pylint: disable=R0902
+ """
+ Element of test package database. It's a container for package
+ informations. It contains two sets of versions for single package.
+ That's meant for updating tests.
+ """
+ def __init__(self, name, epoch, ver, rel, arch, repo,
+ **kwargs):
+ """
+ Arguments prefixed with 'up_' are for newer package.
+ """
+ self._name = name
+ if not epoch or epoch.lower() == "(none)":
+ epoch = "0"
+ self._epoch = epoch
+ self._ver = ver
+ self._rel = rel
+ self._arch = arch
+ self._repo = repo
+ safe = kwargs.get('safe', False)
+ self._up_epoch = epoch if safe else kwargs.get('up_epoch', epoch)
+ self._up_ver = ver if safe else kwargs.get('up_ver' , ver)
+ self._up_rel = rel if safe else kwargs.get('up_rel' , rel)
+ self._up_repo = repo if safe else kwargs.get('up_repo', repo)
+
+ def __str__(self):
+ return self.get_nevra()
+
+ @property
+ def name(self): return self._name #pylint: disable=C0111,C0321
+ @property
+ def epoch(self): return self._epoch #pylint: disable=C0111,C0321
+ @property
+ def ver(self): return self._ver #pylint: disable=C0111,C0321
+ @property
+ def rel(self): return self._rel #pylint: disable=C0111,C0321
+ @property
+ def arch(self): return self._arch #pylint: disable=C0111,C0321
+ @property
+ def repo(self): return self._repo #pylint: disable=C0111,C0321
+ @property
+ def nevra(self): #pylint: disable=C0111,C0321
+ return self.get_nevra(False)
+ @property
+ def evra(self): #pylint: disable=C0111,C0321
+ return self.get_evra(False)
+
+ @property
+ def up_epoch(self): return self._up_epoch #pylint: disable=C0111,C0321
+ @property
+ def up_ver(self): return self._up_ver #pylint: disable=C0111,C0321
+ @property
+ def up_rel(self): return self._up_rel #pylint: disable=C0111,C0321
+ @property
+ def up_repo(self): return self._up_repo #pylint: disable=C0111,C0321
+ @property
+ def up_nevra(self): #pylint: disable=C0111,C0321
+ return self.get_nevra(True)
+ @property
+ def up_evra(self): #pylint: disable=C0111,C0321
+ return self.get_evra(True)
+
+ @property
+ def is_safe(self):
+ """
+ @return True if properties prefixed with up_ matches those without
+ it. In that case a package is suited for non-dangerous tests.
+ """
+ return all( getattr(self, a) == getattr(self, 'up_' + a)
+ for a in ('epoch', 'ver', 'rel', 'repo'))
+
+ def get_nevra(self, newer=True, with_epoch='NOT_ZERO'):
+ """
+ @newer if True, evr part is made from properties prefixed with 'up_'
+ @return pkg nevra string
+ """
+ if newer:
+ attrs = ['name', 'up_epoch', 'up_ver', 'up_rel', 'arch']
+ else:
+ attrs = ['name', 'epoch', 'ver', 'rel', 'arch']
+ return util.make_nevra(*[getattr(self, '_'+a) for a in attrs],
+ with_epoch=with_epoch)
+
+ def get_evra(self, newer=True):
+ """
+ @newer if True, evr part is made from properties prefixed with 'up_'
+ @return pkg nevra string
+ """
+ attrs = [('up_' if newer else '')+a for a in ('epoch', 'ver', 'rel')]
+ attrs.append('arch')
+ return util.make_evra(*[getattr(self, '_'+a) for a in attrs])
+
+class PackageEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, Package):
+ return obj.__dict__
+ return json.JSONEncoder.default(self, obj)
+
+def from_json(json_object):
+ if '_arch' in json_object:
+ kwargs = {k[1:]: v for k, v in json_object.items()}
+ return Package(**kwargs)
+ return json_object
+
diff --git a/src/software/test/repository.py b/src/software/test/repository.py
new file mode 100644
index 0000000..8f88a17
--- /dev/null
+++ b/src/software/test/repository.py
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+# -*- Coding:utf-8 -*-
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details. #
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+"""
+Abstraction of YUM repository for test purposes.
+"""
+
+from collections import defaultdict, namedtuple
+from datetime import datetime
+import os
+import re
+from subprocess import call, check_output
+
+RE_REPO_TAG = re.compile(
+ ur'^repo-(?P<tag>[a-z_-]+)[\s\u00a0]*:'
+ ur'[\s\u00a0]*(?P<value>.*?)[\s\u00a0]*$', re.I | re.U | re.M)
+RE_REPO_INFOS = re.compile(
+ r'^(repo-id\s*:.*?)(?:^\s*$)',
+ re.IGNORECASE | re.MULTILINE | re.DOTALL)
+RE_REPO_CONFIG = re.compile(
+ ur'^(?P<tag>[a-z_-]+)[ \t\u00a0]*='
+ ur'[ \t\u00a0]*((?P<value>.*?)[ \t\u00a0]*)?$',
+ re.I | re.M | re.U)
+RE_REPO_ENABLED = re.compile(
+ r'^enabled\s*=\s*(true|false|0|1|yes|no)\s*$', re.I | re.M)
+
+Repository = namedtuple("Repository", #pylint: disable=C0103
+ # repo properties
+ [ "repoid"
+ , "name"
+ , "status"
+ , "revision"
+ , "tags"
+ , "last_updated"
+ , "pkg_count"
+ , "base_urls"
+ , "metalink"
+ , "mirror_list"
+ , "filename"
+ , "cost"
+ , "gpg_check"
+ , "repo_gpg_check"
+ ])
+
+# example of repo information
+#Repo-id : updates-testing/18/x86_64
+#Repo-name : Fedora 18 - x86_64 - Test Updates
+#Repo-status : enabled
+#Repo-revision: 1360887143
+#Repo-tags : binary-x86_64
+#Repo-updated : Fri Feb 15 02:02:20 2013
+#Repo-pkgs : 13 808
+#Repo-size : 14 G
+#Repo-baseurl : file:///mnt/globalsync/fedora/linux/updates/testing/18/x86_64
+#Repo-expire : 21 600 second(s) (last: Fri Feb 15 10:32:13 2013)
+#Repo-filename: ///etc/yum.repos.d/fedora-updates-testing.repo
+
+def _parse_repo_file(repo_name):
+ """
+ Parse output of yum-config-manager command for single repository.
+ @return dictionary with key-value pairs suitable for passing to Repository
+ constructor.
+ """
+ cmd = ["yum-config-manager", repo_name]
+ out = check_output(cmd).decode('utf-8')
+ result = {}
+ for match in RE_REPO_CONFIG.finditer(out):
+ tag = match.group('tag')
+ value = match.group('value')
+ if tag == "gpgcheck":
+ result["gpg_check"] = value.lower() == "true"
+ elif tag == "repo_gpgcheck":
+ result["repo_gpg_check"] = value.lower() == "true"
+ elif tag == "timeout":
+ result[tag] = float(value)
+ elif tag == "metadata_expire":
+ result[tag] = int(value)
+ elif tag == "cost":
+ result[tag] = int(value)
+ else:
+ continue
+ return result
+
+def make_repo(repo_info):
+ """
+ Makes a Repository instance from string dumped by yum repoinfo command.
+ """
+ metadata = defaultdict(lambda : None)
+ metadata["base_urls"] = []
+ fields = set(Repository._fields)
+ for match in RE_REPO_TAG.finditer(repo_info):
+ tag = match.group('tag')
+ value = match.group('value')
+ try:
+ new_tag = \
+ { 'id' : 'repoid'
+ , 'updated' : 'last_updated'
+ , 'pkgs' : 'pkg_count'
+ , 'baseurl' : 'base_urls'
+ , 'mirrors' : 'mirror_list'
+ }[tag]
+ except KeyError:
+ if tag not in fields:
+ continue # unexpeted, or unwanted tag found
+ new_tag = tag
+ if new_tag == 'repoid':
+ metadata[new_tag] = value.split('/')[0]
+ elif new_tag == "base_urls":
+ metadata[new_tag].append(value)
+ elif new_tag == "last_updated":
+ metadata[new_tag] = datetime.strptime(
+ value, "%a %b %d %H:%M:%S %Y")
+ elif new_tag == "pkg_count":
+ metadata[new_tag] = int(
+ u"".join(re.split(ur'[\s\u00a0]+', value, re.UNICODE)))
+ elif new_tag == "status":
+ metadata[new_tag] = value == "enabled"
+ elif new_tag == 'revision':
+ metadata[new_tag] = int(value)
+ else:
+ metadata[new_tag] = value
+ config_items = _parse_repo_file(metadata['repoid'])
+ for field in Repository._fields:
+ if not field in metadata:
+ metadata[field] = config_items.get(field, None)
+ return Repository(**dict((f, metadata[f]) for f in Repository._fields))
+
+def get_repo_database():
+ """
+ @return list of Repository instances for all configured repositories.
+ """
+ result = []
+ cmd = ["yum", "-q", "-C", "repoinfo", "all"]
+ repo_infos = check_output(cmd).decode('utf-8')
+ for match in RE_REPO_INFOS.finditer(repo_infos):
+ result.append(make_repo(match.group(1)))
+ return result
+
+def is_repo_enabled(repo):
+ """
+ @return True, if repository is enabled
+ """
+ if isinstance(repo, Repository):
+ repo = repo.repoid
+ cmd = ["yum-config-manager", repo]
+ out = check_output(cmd)
+ match = RE_REPO_ENABLED.search(out)
+ return bool(match) and match.group(1).lower() in {"true", "yes", "1"}
+
+def set_repo_enabled(repo, enable):
+ """
+ Enables or disables repository in its configuration file.
+ """
+ if isinstance(repo, Repository):
+ repo = repo.repoid
+ cmd = ["yum-config-manager", "--enable" if enable else "--disable", repo]
+ with open('/dev/null', 'w') as out:
+ call(cmd, stdout=out, stderr=out)
+
diff --git a/src/software/test/rpmcache.py b/src/software/test/rpmcache.py
index e1f15d9..18b4779 100644
--- a/src/software/test/rpmcache.py
+++ b/src/software/test/rpmcache.py
@@ -18,49 +18,58 @@
#
# Authors: Radek Novacek <rnovacek@redhat.com>
# Authors: Michal Minar <miminar@redhat.com>
+
"""
Creation and manipulation utilities with rpm cache for software tests.
"""
+
import copy
-import datetime
import os
-import pickle
+import json
+import random
import re
from collections import defaultdict
from subprocess import call, check_output, CalledProcessError
+from package import Package, PackageEncoder, from_json
+import util
+
DB_BACKUP_FILE = 'lmi_software_test_cache'
RE_AVAIL_PKG = re.compile(
r'^(?P<name>[^\s]+)\.(?P<arch>[a-zA-Z0-9_]+)'
- r'\s+(?P<epoch>([0-9]+:)?)(?P<version>[a-zA-Z0-9._+-]+)'
- r'-(?P<release>[a-zA-Z0-9_.]+)\s+'
- r'(?P<repository>[a-zA-Z0-9_-]+)\s*$', re.MULTILINE)
+ r'\s+((?P<epoch>[0-9]+):)?(?P<ver>[a-zA-Z0-9._+-]+)'
+ r'-(?P<rel>[a-zA-Z0-9_.]+)\s+'
+ r'(?P<repo>[a-zA-Z0-9_-]+)\s*$', re.MULTILINE)
# this won't match the last entry, unless "package\n" is not appended
# at the end of the string
RE_PKG_DEPS = re.compile(
r'^package:\s*(?P<name>[^\s]+)\.(?P<arch>[a-zA-Z0-9_]+)'
- r'\s+(?P<epoch>([0-9]+:)?)(?P<version>[a-zA-Z0-9._+-]+)'
- r'-(?P<release>[a-zA-Z0-9_.]+)\s+(?P<dep_list>.*?)'
+ r'\s+((?P<epoch>[0-9]+):)?(?P<ver>[a-zA-Z0-9._+-]+)'
+ r'-(?P<rel>[a-zA-Z0-9_.]+)\s+(?P<dep_list>.*?)'
r'(?=^package|\Z)', re.MULTILINE | re.DOTALL)
RE_DEPS_PROVIDERS = re.compile(
r'^\s*dependency:\s*(?P<dependency>.+?)\s*'
- r'(?P<providers>(^\s+provider:.+?$)+)', re.MULTILINE | re.IGNORECASE)
+ r'(?P<providers>(^\s+unsatisfied\s+dependency$)|(^\s+provider:.+?$)+)',
+ re.MULTILINE | re.IGNORECASE)
+RE_DEPS_UNSATISFIED = re.compile(r'^\s+unsatisfied\s+dep.*$', re.IGNORECASE)
RE_PROVIDER = re.compile(
r'^\s+provider:\s*(?P<name>[^\s]+)\.(?P<arch>[a-zA-Z0-9_]+)'
- r'\s+(?P<epoch>([0-9]+:)?)(?P<version>[a-zA-Z0-9._+-]+)'
- r'-(?P<release>[a-zA-Z0-9_.]+)\s*$', re.IGNORECASE | re.MULTILINE)
+ r'\s+((?P<epoch>[0-9]+):)?(?P<ver>[a-zA-Z0-9._+-]+)'
+ r'-(?P<rel>[a-zA-Z0-9_.]+)\s*$', re.IGNORECASE | re.MULTILINE)
RE_PKG_INFO = re.compile(
r'^Name\s*:\s*(?P<name>[^\s]+).*?'
r'^(Epoch\s*:\s*(?P<epoch>[0-9]+)\s+)?'
- r'^Version\s*:\s*(?P<version>[a-zA-Z0-9._+-]+)\s+'
- r'^Release\s*:\s*(?P<release>[^\s]+)\s+.*?'
+ r'^Version\s*:\s*(?P<ver>[a-zA-Z0-9._+-]+)\s+'
+ r'^Release\s*:\s*(?P<rel>[^\s]+)\s+.*?'
r'^Size\s*:\s*(?P<size>\d+(\.\d+)?)( *(?P<units>[kMG]))?',
re.MULTILINE | re.DOTALL | re.IGNORECASE)
RE_REPO = re.compile(
r'(?:^\*?)(?P<name>[^\s/]+\b)(?!\s+id)', re.MULTILINE | re.IGNORECASE)
-# maximum number of packages, that will be selected for testing
+# Maximum number of packages, that will be selected for testing / 2
+# There are 2 sets of test packages (safe and dangerous). When running
+# in dangerous mode, the resulting number will be twice as much.
MAX_PKG_DB_SIZE = 10
# step used to iterate over package names used to check for thery dependencies
# it's a number of packages, that will be passed to yum command at once
@@ -78,104 +87,6 @@ class MissingRPM(InvalidTestCache):
InvalidTestCache.__init__(self,
"Missing package '%s' in test cache!"%pkg_name)
-def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
- """
- @param with_epoch may be one of:
- "NOT_ZERO" - include epoch only if it's not zero
- "ALWAYS" - include epoch always
- "NEVER" - do not include epoch at all
- """
- estr = ''
- if with_epoch.lower() == "always":
- estr = epoch
- elif with_epoch.lower() == "not_zero":
- if epoch != "0":
- estr = epoch
- if len(estr):
- estr += ":"
- return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
-
-def run_yum(*params, **kwargs):
- """
- Runs yum with params and returns its output
- It's here especially to allow pass a repolist argument, that
- specifies list of repositories, to run the command on.
- """
- cmd = ['yum'] + list(params)
- repolist = kwargs.get('repolist', [])
- if repolist:
- cmd += ['--disablerepo=*']
- cmd += ['--enablerepo='+r for r in repolist]
- try:
- return check_output(cmd)
- except Exception:
- import pdb;pdb.set_trace()
-
-class Package(object): #pylint: disable=R0902
- """
- Element of test package database. It's a container for package
- informations. It contains two sets of versions for single package.
- That's meant for updating tests.
- """
- def __init__(self, name, epoch, ver, rel, arch, repo,
- up_epoch, up_ver, up_rel, up_repo):
- """
- Arguments prefixed with 'up_' are for newer package.
- """
- self._name = name
- self._epoch = epoch
- self._ver = ver
- self._rel = rel
- self._arch = arch
- self._repo = repo
- self._up_epoch = up_epoch
- self._up_ver = up_ver
- self._up_rel = up_rel
- self._up_repo = up_repo
-
- def __str__(self):
- return self.get_nevra()
-
- @property
- def name(self): return self._name #pylint: disable=C0111,C0321
- @property
- def epoch(self): return self._epoch #pylint: disable=C0111,C0321
- @property
- def ver(self): return self._ver #pylint: disable=C0111,C0321
- @property
- def rel(self): return self._rel #pylint: disable=C0111,C0321
- @property
- def arch(self): return self._arch #pylint: disable=C0111,C0321
- @property
- def repo(self): return self._repo #pylint: disable=C0111,C0321
- @property
- def nevra(self): #pylint: disable=C0111,C0321
- return self.get_nevra(True)
-
- @property
- def up_epoch(self): return self._up_epoch #pylint: disable=C0111,C0321
- @property
- def up_ver(self): return self._up_ver #pylint: disable=C0111,C0321
- @property
- def up_rel(self): return self._up_rel #pylint: disable=C0111,C0321
- @property
- def up_repo(self): return self._up_repo #pylint: disable=C0111,C0321
- @property
- def up_nevra(self): #pylint: disable=C0111,C0321
- return self.get_nevra(True)
-
- def get_nevra(self, newer=True, with_epoch='NOT_ZERO'):
- """
- @newer if True, evr part is made from properties prefixed with 'up_'
- @return pkg nevra string
- """
- if newer:
- attrs = ['name', 'up_epoch', 'up_ver', 'up_rel', 'arch']
- else:
- attrs = ['name', 'epoch', 'ver', 'rel', 'arch']
- return make_nevra(*[getattr(self, '_'+a) for a in attrs],
- with_epoch=with_epoch)
-
def _match_nevr(match):
"""
@param match is a regexp match object with parsed rpm package
@@ -183,22 +94,33 @@ def _match_nevr(match):
"""
epoch = match.group('epoch')
return ( match.group('name')
- , epoch[:-1] if epoch else "0"
- , match.group('version')
- , match.group('release'))
+ , epoch if epoch and epoch.lower() != "(none)" else "0"
+ , match.group('ver')
+ , match.group('rel'))
-def _match2pkg(match):
+def _match2pkg(match, safe=False):
"""
@param match is a regexp match object with attributes:
name, epoch, version, release, arch
+ @param safe if True, the packe will have up_* properties equal to
+ non-prefixed ones, otherwise they will be set to None
@return instance of Package
"""
+ kwargs = {}
+ if safe is True:
+ kwargs['safe'] = True
+ else:
+ for attr in ('epoch', 'ver', 'rel', 'repo'):
+ kwargs['up_'+attr] = None
+ epoch = match.group('epoch')
+ if not epoch or epoch.lower() == "(none)":
+ epoch = '0'
return Package(
match.group('name'),
- match.group('epoch')[:-1] if match.group('epoch') else '0',
- match.group('version'), match.group('release'),
- match.group('arch'), match.groupdict().get('repository', None),
- None, None, None, None)
+ epoch,
+ match.group('ver'), match.group('rel'),
+ match.group('arch'), match.groupdict().get('repo', None),
+ **kwargs)
def _filter_duplicates(installed, avail_str):
"""
@@ -213,7 +135,7 @@ def _filter_duplicates(installed, avail_str):
dups_list = []
cur_package_matches = []
prev_match = None
- system_arch = get_system_architecture()
+ system_arch = util.get_system_architecture()
for match in RE_AVAIL_PKG.finditer(avail_str):
if ( _match_nevr(match) in [ _match_nevr(m)
for m in cur_package_matches]
@@ -246,12 +168,14 @@ def _check_single_pkg_deps(
"""
for match_deps in RE_DEPS_PROVIDERS.finditer(dependencies_str):
providers = []
+ if RE_DEPS_UNSATISFIED.search(match_deps.group('providers')):
+ return False
for match_dep in RE_PROVIDER.finditer(match_deps.group('providers')):
if match_dep.group('name') not in installed:
continue
providers.append(_match2pkg(match_dep))
for provider in providers:
- if is_installed(provider, False):
+ if is_pkg_installed(provider, False):
break
else: # no provider is installed
return False
@@ -261,7 +185,7 @@ def _check_pkg_dependencies(
installed,
dup_list,
number_of_packages=MAX_PKG_DB_SIZE,
- repolist=[]):
+ repolist=None):
"""
Finds packages from dup_list with satisfied (installed) dependencies.
@param installed is a set of installed package names
@@ -274,7 +198,7 @@ def _check_pkg_dependencies(
yum_params = yum_params[:1]
for dups in dups_part:
yum_params.extend([d.get_nevra(newer=False) for d in dups])
- deplist_str = run_yum(*yum_params, repolist=repolist)
+ deplist_str = util.run_yum(*yum_params, repolist=repolist)
matches = RE_PKG_DEPS.finditer(deplist_str)
prev_match = None
for pkgs in dups_part:
@@ -300,14 +224,14 @@ def _check_pkg_dependencies(
break
return dups_no_deps
-def _sorted_db_by_size(pkgdb, repolist=[]):
+def _sorted_db_by_size(pkgdb, repolist=None):
"""
@param pkgdb is a list of lists of packages with common name
@return sorted instances of Package according to their size
"""
yum_params = ['info', '--showduplicates']
yum_params.extend([ps[0].name for ps in pkgdb])
- info_str = run_yum(*yum_params, repolist=repolist)
+ info_str = util.run_yum(*yum_params, repolist=repolist)
pkg_sizes = {}
# to get correct ordering from "yum info" command
# { pkg_name : [(epoch, version, release), ... ] }
@@ -329,7 +253,7 @@ def _sorted_db_by_size(pkgdb, repolist=[]):
if not epoch:
epoch = "0"
pkg_version_order[pkg_name].append((
- epoch, info_match.group('version'), info_match.group('release')))
+ epoch, info_match.group('ver'), info_match.group('rel')))
pkgdb = sorted(pkgdb, key=lambda pkgs: pkg_sizes[pkgs[0].name])[
:MAX_PKG_DB_SIZE]
@@ -350,7 +274,7 @@ def _get_repo_list():
repos_str = check_output(['yum', 'repolist', '-q'])
return RE_REPO.findall(repos_str)
-def _download_pkgdb(repolist, pkgdb, cache_dir=None):
+def _download_dangerous(repolist, pkgdb, cache_dir=None):
"""
Downloads all rpm packages (old and newer versions) from package database
to current directory.
@@ -385,32 +309,6 @@ def _make_rpm_path(pkg, cache_dir='', newer=True, without_epoch=False):
with_epoch='NEVER' if without_epoch else 'NOT_ZERO')
return os.path.join(cache_dir, nevra) + '.rpm'
-def is_installed(pkg, newer=True):
- """
- Check, whether package is installed.
- """
- if not isinstance(pkg, Package):
- return call(["rpm", "--quiet", "-q", pkg]) == 0
- else:
- try:
- cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}", pkg.get_nevra(
- newer, with_epoch='NEVER') ]
- out = check_output(cmd)
- epoch, nvra = out.split(':') #pylint: disable=E1103
- if not epoch or epoch == "(none)":
- epoch = "0"
- return ( epoch == pkg.epoch
- and nvra == pkg.get_nevra(newer=newer, with_epoch="NEVER"))
- except CalledProcessError:
- return False
-
-def rpm_exists(pkg, cache_dir='', newer=True):
- """
- @return True, when rpm package is in cache.
- """
- return ( os.path.exists(_make_rpm_path(pkg, cache_dir, newer))
- or os.path.exists(_make_rpm_path(pkg, cache_dir, newer, True)))
-
def get_rpm_name(pkg, cache_dir='', newer=True):
"""
Some packages do not have epoch in their name, even if it's higher than
@@ -425,56 +323,127 @@ def get_rpm_name(pkg, cache_dir='', newer=True):
return path
raise MissingRPM(pkg.name)
-def get_system_architecture():
+def rpm_exists(pkg, cache_dir='', newer=True):
+ """
+ @return True, when rpm package is in cache.
+ """
+ return ( os.path.exists(_make_rpm_path(pkg, cache_dir, newer))
+ or os.path.exists(_make_rpm_path(pkg, cache_dir, newer, True)))
+
+
+def remove_pkg(pkg, *args):
+ """
+ Remove package with rpm command.
+ @param pkg is either instance of Package or package name
+ @param args is a list of parameters for rpm command
+ """
+ if isinstance(pkg, Package):
+ pkg = pkg.name
+ call(["rpm", "--quiet"] + list(args) + ["-e", pkg])
+
+def install_pkg(pkg, newer=True, repolist=None):
+ """
+ Install a specific package.
+ @param pkg is either package name or instance of Package
+ In latter case, a specific version is installed.
+ @param repolist is a list of repositories, that should be
+ used for downloading, if using yum
+ when empty, all enabled repositories are used
+ """
+ if isinstance(pkg, Package):
+ try:
+ rpm_name = get_rpm_name(pkg, newer=newer)
+ call(["rpm", "--quiet", "-i", rpm_name])
+ return
+ except MissingRPM:
+ pass
+ pkg = pkg.name
+ util.run_yum('-q', '-y', 'install', pkg, repolist=repolist)
+
+def ensure_pkg_installed(pkg, newer=True, repolist=None):
+ """
+ Ensures, that specific version of package is installed. If other
+ version is installed, it is removed and reinstalled.
+ """
+ if not isinstance(pkg, Package):
+ raise TypeError("pkg must be a Package instance")
+ if not is_pkg_installed(pkg, newer):
+ if is_pkg_installed(pkg.name):
+ remove_pkg(pkg.name)
+ install_pkg(pkg, newer, repolist)
+
+def verify_pkg(pkg):
+ """
+ @return output of command rpm, with verification output for package
+ """
+ if isinstance(pkg, basestring):
+ name = pkg
+ elif isinstance(pkg, Package):
+ name = pkg.name
+ else:
+ raise TypeError("pkg must be either package name or Package instance")
+ return call(["rpm", "--quiet", "-Va", name]) == 0
+
+def has_pkg_config_file(pkg, file_path):
+ """
+ @return True, if file_path is a configuration file of package pkg.
+ """
+ cmd = ['rpm', '-qc', pkg.name]
+ out = check_output(cmd)
+ return file_path in set(out.splitlines()) #pylint: disable=E1103
+
+def has_pkg_doc_file(pkg, file_path):
"""
- @return the system architecture name as seen by rpm
+ @return True, if file_path is a documentation file of package pkg.
"""
- return check_output(['rpm', '-q', '--qf', '%{ARCH}\n', 'rpm'])
+ cmd = ['rpm', '-qd', pkg.name]
+ out = check_output(cmd)
+ return file_path in set(out.splitlines()) #pylint: disable=E1103
+
+def is_pkg_installed(pkg, newer=True):
+ """
+ Check, whether package is installed.
+ """
+ if not isinstance(pkg, Package):
+ return call(["rpm", "--quiet", "-q", pkg]) == 0
+ else:
+ cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}\n", pkg.get_nevra(
+ newer, with_epoch='NEVER') ]
+ try:
+ out = check_output(cmd).splitlines()[0]
+ epoch, nvra = out.split(':') #pylint: disable=E1103
+ if not epoch or epoch.lower() == "(none)":
+ epoch = "0"
+ return ( epoch == getattr(pkg, 'up_epoch' if newer else 'epoch')
+ and nvra == pkg.get_nevra(newer=newer, with_epoch="NEVER"))
+ except CalledProcessError:
+ return False
-def write_pkgdb(pkgdb, cache_dir=''):
+def write_pkgdb(safe_pkgs, dangerous_pkgs, cache_dir=''):
"""
Writes package database into a file named DB_BACKUP_FILE.
"""
with open(os.path.join(cache_dir, DB_BACKUP_FILE), 'w') as db_file:
- pickle.dump((datetime.datetime.now(), pkgdb), db_file)
+ data = (safe_pkgs, dangerous_pkgs)
+ json.dump(data, db_file, cls=PackageEncoder,
+ sort_keys=True, indent=4, separators=(',', ': '))
def load_pkgdb(cache_dir=''):
"""
This is inverse function to _write_pkgdb().
- @return package database loaded from file
+ @return (safe, dangerous) package lists loaded from file
"""
with open(os.path.join(cache_dir, DB_BACKUP_FILE), 'r') as db_file:
- date_time, pkgdb = pickle.load(db_file)
+ safe, dangerous = json.load(db_file, object_hook=from_json)
#print "Loaded package database from: %s" % date_time
- return pkgdb
+ return safe, dangerous
-def get_pkg_database(force_update=False, use_cache=True,
- cache_dir='',
- repolist=[]):
+def make_dangerous_list(installed, repolist=None):
"""
- Checks yum database for available packages, that have at least two
- different versions in repositories. Only not installed ones with
- all of their dependencies intalled are selected.
- And from those, few of the smallest are downloaded as rpms.
- @return list of instances of Package of selected packages
+ This makes a list of instances of Package for dangerous tests.
"""
- if ( use_cache and not force_update
- and os.path.exists(os.path.join(cache_dir, DB_BACKUP_FILE))):
- pkgdb = load_pkgdb(cache_dir)
- valid_db = True
- for pkg in pkgdb:
- if ( not rpm_exists(pkg, cache_dir, False)
- or not rpm_exists(pkg, cache_dir, True)):
- valid_db = False
- #print "Old package database is not valid"
- break
- if valid_db:
- return pkgdb
- #print "Getting installed packages"
- installed = set(check_output( #pylint: disable=E1103
- ['rpm', '-qa', '--qf=%{NAME}\n']).splitlines())
#print "Getting all available packages"
- avail_str = run_yum('list', 'available', '--showduplicates',
+ avail_str = util.run_yum('list', 'available', '--showduplicates',
repolist=repolist)
# list of lists of packages with the same name, longer than 2
#print "Finding duplicates"
@@ -485,11 +454,88 @@ def get_pkg_database(force_update=False, use_cache=True,
number_of_packages=MAX_PKG_DB_SIZE*5,
repolist=repolist)
#print "Selecting the smallest ones"
- pkgdb = _sorted_db_by_size(selected, repolist=repolist)
+ return _sorted_db_by_size(selected, repolist=repolist)
+
+def make_safe_list(installed, exclude=None):
+ """
+ Makes list of installed packages for non-dangerous tests.
+ @param installed is a list of installed packages names
+ @param exclude is a list of package names, that won't appear in result
+ """
+ if exclude is None:
+ exclude = set()
+ base = list(installed)
+ random.shuffle(base)
+ res = []
+ i = 0
+ while len(res) < MAX_PKG_DB_SIZE and i < len(base):
+ name = base[i]
+ i += 1
+ if name in exclude:
+ continue
+ cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}\n", name ]
+ envra = check_output(cmd).splitlines()[0]
+ res.append(_match2pkg(util.RE_ENVRA.match(envra), safe=True))
+ return res
+
+def get_pkg_database(
+ force_update=False,
+ use_cache=True,
+ dangerous=False,
+ cache_dir='',
+ repolist=None):
+ """
+ Checks yum database for available packages, that have at least two
+ different versions in repositories. Only not installed ones with
+ all of their dependencies intalled are selected.
+ And from those, few of the smallest are downloaded as rpms.
+ @param dangerous whether to load available, not installed
+ packages for dangerous tests
+ @return (safe, dangerous)
+ where
+ safe is a list of instances of Package, representing installed
+ software, these should be used for not-dangerous tests;
+ both older
+ dangerous is a list of instances of Package of selected packages,
+ that are not installed, but available; instances contain
+ both newer and older version of package
+ """
+ dangerous_pkgs = []
+ if ( use_cache and not force_update
+ and os.path.exists(os.path.join(cache_dir, DB_BACKUP_FILE))):
+ safe_pkgs, dangerous_pkgs = load_pkgdb(cache_dir)
+ valid_db = True
+ if len(safe_pkgs) < MAX_PKG_DB_SIZE:
+ valid_db = False
+ elif not dangerous and len(dangerous_pkgs) > 0:
+ dangerous_pkgs = []
+ elif dangerous and len(dangerous_pkgs) == 0:
+ valid_db = False
+ else:
+ for pkg in safe_pkgs:
+ if not is_pkg_installed(pkg):
+ valid_db = False
+ break
+ for pkg in dangerous_pkgs:
+ if ( not rpm_exists(pkg, cache_dir, False)
+ or not rpm_exists(pkg, cache_dir, True)):
+ valid_db = False
+ #print "Old package database is not valid"
+ break
+ if valid_db:
+ return (safe_pkgs, dangerous_pkgs)
+ #print "Getting installed packages"
+ installed = set(check_output( #pylint: disable=E1103
+ ['rpm', '-qa', '--qf=%{NAME}\n']).splitlines())
+ if dangerous:
+ dangerous_pkgs = make_dangerous_list(installed)
+ safe_pkgs = make_safe_list(installed, exclude=set(
+ pkg.name for pkg in dangerous_pkgs))
+
if use_cache:
- repolist = _get_repo_list()
- _download_pkgdb(repolist, pkgdb, cache_dir)
+ repolist = _get_repo_list() if repolist in (None, []) else repolist
+ _download_dangerous(repolist, dangerous_pkgs, cache_dir)
#print "Backing up database information"
- write_pkgdb(pkgdb, cache_dir)
- return pkgdb
+ write_pkgdb(safe_pkgs, dangerous_pkgs, cache_dir)
+ return (safe_pkgs, dangerous_pkgs)
diff --git a/src/software/test/run.py b/src/software/test/run.py
index e58945c..74c2cf3 100755
--- a/src/software/test/run.py
+++ b/src/software/test/run.py
@@ -68,6 +68,7 @@ def parse_cmd_line():
parser.add_argument("-p", "--password",
default=os.environ.get("LMI_CIMOM_PASSWORD", ''),
help="User's password.")
+
dangerous_group = parser.add_mutually_exclusive_group()
dangerous_group.add_argument("--run-dangerous", action="store_true",
default=(os.environ.get('LMI_RUN_DANGEROUS', '0') == '1'),
@@ -78,6 +79,17 @@ def parse_cmd_line():
dest="run_dangerous",
default=os.environ.get('LMI_RUN_DANGEROUS', '0') == '1',
help="Disable dangerous tests.")
+
+ tedious_group = parser.add_mutually_exclusive_group()
+ tedious_group.add_argument("--run-tedious", action="store_true",
+ default=(os.environ.get('LMI_RUN_TEDIOUS', '0') == '1'),
+ help="Run also tedious (long running) for this machine."
+ " Overrides environment variable LMI_RUN_TEDIOUS.")
+ tedious_group.add_argument('--no-tedious', action="store_false",
+ dest="run_tedious",
+ default=os.environ.get('LMI_RUN_TEDIOUS', '0') == '1',
+ help="Disable tedious tests.")
+
cache_group = parser.add_mutually_exclusive_group()
cache_group.add_argument("-c", "--use-cache", action="store_true",
default=(os.environ.get('LMI_SOFTWARE_USE_CACHE', '0') == '1'),
@@ -160,6 +172,8 @@ def prepare_environment(args):
os.environ['LMI_CIMOM_PASSWORD'] = args.password
os.environ['LMI_RUN_DANGEROUS'] = (
'1' if args.run_dangerous else '0')
+ os.environ["LMI_RUN_TEDIOUS"] = (
+ '1' if args.run_tedious else '0')
os.environ['LMI_SOFTWARE_USE_CACHE'] = '1' if args.use_cache else '0'
if args.use_cache:
os.environ['LMI_SOFTWARE_CACHE_DIR'] = CACHE_DIR
@@ -270,9 +284,12 @@ def main():
repolist = args.test_repos.split(',')
else:
repolist = []
- rpmcache.get_pkg_database(args.force_update, args.use_cache,
- CACHE_DIR, repolist=repolist)
- #rpmcache.make_pkg_database(packages
+ rpmcache.get_pkg_database(
+ args.force_update,
+ args.use_cache,
+ dangerous=args.run_dangerous,
+ cache_dir=CACHE_DIR,
+ repolist=repolist)
prepare_environment(args)
test_program = unittest.main(argv=ut_args,
testLoader=LMITestLoader(), exit=False)
diff --git a/src/software/test/test_hosted_software_collection.py b/src/software/test/test_hosted_software_collection.py
new file mode 100755
index 0000000..ccd00b8
--- /dev/null
+++ b/src/software/test/test_hosted_software_collection.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_MemberOfSoftwareCollection provider.
+"""
+
+import pywbem
+import socket
+import unittest
+
+import base
+
+class TestHostedSoftwareCollection(base.SoftwareBaseTestCase):
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_HostedSoftwareCollection"
+ KEYS = ("Antecedent", "Dependent")
+
+ @classmethod
+ def needs_pkgdb(cls):
+ return False
+
+ def make_op(self):
+ """
+ @param ses SoftwareElementState property value
+ @return object path of SoftwareIdentity
+ """
+ objpath = self.objpath.copy()
+ objpath["Antecedent"] = pywbem.CIMInstanceName(
+ classname="Linux_ComputerSystem",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "CreationClassName" : "Linux_ComputerSystem",
+ "Name" : socket.gethostname()
+ }))
+ objpath["Dependent"] = pywbem.CIMInstanceName(
+ classname="LMI_SystemSoftwareCollection",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "InstanceID" : "LMI:SystemSoftwareCollection"
+ }))
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ objpath = self.make_op()
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ self.assertEqual(inst.path, objpath)
+ self.assertEqual(sorted(inst.properties.keys()), sorted(self.KEYS))
+ self.assertEqual(objpath, inst.path)
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+
+ # try with CIM_ prefix
+ antecedent = objpath["Antecedent"].copy()
+ objpath["Antecedent"].classname = "CIM_ComputerSystem"
+
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ objpath["Antecedent"] = antecedent.copy()
+ self.assertEqual(objpath, inst.path)
+ self.assertEqual(sorted(inst.properties.keys()), sorted(self.KEYS))
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+
+ # try with CIM_ prefix also for CreationClassName
+ objpath["Antecedent"]["CreationClassName"] = "CIM_ComputerSystem"
+
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ objpath["Antecedent"] = antecedent.copy()
+ self.assertEqual(objpath, inst.path)
+ self.assertEqual(sorted(inst.properties.keys()), sorted(self.KEYS))
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+
+ def test_enum_instances(self):
+ """
+ Tests EnumInstances call on installed packages.
+ """
+ objpath = self.make_op()
+ insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ self.assertEqual(1, len(insts))
+ self.assertEqual(objpath, insts[0].path)
+ self.assertEqual(sorted(insts[0].properties.keys()), sorted(self.KEYS))
+ self.assertEqual(objpath, insts[0].path)
+ for key in self.KEYS:
+ self.assertEqual(insts[0][key], insts[0].path[key])
+
+ def test_enum_instance_names(self):
+ """
+ Tests EnumInstanceNames call on installed packages.
+ """
+ objpath = self.make_op()
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertEqual(1, len(inames))
+ self.assertEqual(objpath, inames[0])
+
+ def test_get_antecedent_referents(self):
+ """
+ Test ReferenceNames for ComputerSystem.
+ """
+ objpath = self.make_op()
+ refs = self.conn.AssociatorNames(
+ Role="Antecedent",
+ ObjectName=objpath["Antecedent"],
+ ResultRole="Dependent",
+ ResultClass='LMI_SystemSoftwareCollection')
+ self.assertEqual(len(refs), 1)
+ ref = refs[0]
+ self.assertEqual(objpath["Dependent"], ref)
+
+ @base.mark_dangerous
+ def test_get_dependent_referents(self):
+ """
+ Test ReferenceNames for SystemSoftwareCollection.
+ """
+ objpath = self.make_op()
+ refs = self.conn.AssociatorNames(
+ ObjectName=objpath["Dependent"],
+ Role="Dependent",
+ ResultRole="Antecedent",
+ ResultClass="Linux_ComputerSystem")
+ self.assertEqual(1, len(refs))
+ ref = refs[0]
+ self.assertEqual(objpath["Antecedent"], ref)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestHostedSoftwareCollection)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_hosted_software_identity_resource.py b/src/software/test/test_hosted_software_identity_resource.py
new file mode 100755
index 0000000..6a60e61
--- /dev/null
+++ b/src/software/test/test_hosted_software_identity_resource.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_HostedSoftwareIdentityResource provider.
+"""
+
+import pywbem
+import socket
+import unittest
+
+import base
+
+class TestHostedSoftwareIdentityResource(base.SoftwareBaseTestCase):
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_HostedSoftwareIdentityResource"
+ KEYS = ("Antecedent", "Dependent")
+
+ @classmethod
+ def needs_pkgdb(cls):
+ return False
+
+ @classmethod
+ def needs_repodb(cls):
+ return True
+
+ def make_op(self, repo):
+ """
+ @return object path of HostedSoftwareIdentityResource association
+ """
+ objpath = self.objpath.copy()
+ objpath["Antecedent"] = pywbem.CIMInstanceName(
+ classname="Linux_ComputerSystem",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "CreationClassName" : "Linux_ComputerSystem",
+ "Name" : socket.gethostname()
+ }))
+ objpath["Dependent"] = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentityResource",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "CreationClassName" : "LMI_SoftwareIdentityResource",
+ "SystemCreationClassName" : "Linux_ComputerSystem",
+ "SystemName" : socket.gethostname(),
+ "Name" : repo.repoid
+ }))
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on repositories from our rpm cache.
+ """
+ for repo in self.repodb:
+ objpath = self.make_op(repo)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(objpath, inst.path,
+ "Object paths should match for repo %s" % repo.repoid)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ 'OP is missing \"%s\" key for repo "%s".' % (key, repo.repoid))
+ self.assertEqual(inst[key], inst.path[key],
+ 'Key property "%s" does not match for repo "%s"!' %
+ (key, repo.repoid))
+
+ def test_enum_instance_names(self):
+ """
+ Tests EnumInstanceNames call on repositories from our cache.
+ """
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ repos = { r.repoid: r for r in self.repodb }
+ self.assertEqual(len(repos), len(inames))
+ for iname in inames:
+ self.assertIn('Dependent', iname)
+ self.assertIn('Name', iname['Dependent'])
+ self.assertIn(iname['Dependent']['Name'], repos)
+ objpath = self.make_op(repos[iname['Dependent']['Name']])
+ self.assertEqual(objpath, iname)
+
+ def test_enum_instances(self):
+ """
+ Tests EnumInstanceNames call on repositories from our cache.
+ """
+ inames = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ repos = { r.repoid: r for r in self.repodb }
+ self.assertEqual(len(repos), len(inames))
+ for inst in inames:
+ self.assertIn('Dependent', inst)
+ self.assertIn('Name', inst['Dependent'])
+ repoid = inst['Dependent']['Name']
+ self.assertIn(repoid, repos)
+ objpath = self.make_op(repos[repoid])
+ self.assertEqual(objpath, inst.path)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ 'OP is missing \"%s\" key for repo "%s".' % (key, repoid))
+ self.assertEqual(inst[key], inst.path[key],
+ 'Key property "%s" does not match for repo "%s"!' %
+ (key, repoid))
+
+ def test_get_antecedent_referents(self):
+ """
+ Test ReferenceNames for ComputerSystem.
+ """
+ if not self.repodb:
+ return
+ repo = self.repodb[0]
+ objpath = self.make_op(repo)
+ refs = self.conn.AssociatorNames(
+ Role="Antecedent",
+ ObjectName=objpath["Antecedent"],
+ ResultRole="Dependent",
+ ResultClass="LMI_SoftwareIdentityResource")
+ repos = {r.repoid: r for r in self.repodb}
+ for ref in refs:
+ self.assertIsInstance(ref, pywbem.CIMInstanceName)
+ self.assertEqual(ref.namespace, 'root/cimv2')
+ self.assertEqual(ref.classname, "LMI_SoftwareIdentityResource")
+ self.assertIn("Name", ref.keys())
+ objpath = self.make_op(repos[ref["Name"]])
+ self.assertEqual(objpath["Dependent"], ref)
+ del repos[ref["Name"]]
+ self.assertEqual(0, len(repos))
+
+ def test_get_dependent_referents(self):
+ """
+ Test ReferenceNames for repository.
+ """
+ for repo in self.repodb:
+ objpath = self.make_op(repo=repo)
+ refs = self.conn.AssociatorNames(
+ Role="Dependent",
+ ObjectName=objpath["Dependent"],
+ ResultRole="Antecedent",
+ ResultClass="Linux_ComputerSystem")
+ self.assertEqual(len(refs), 1)
+ self.assertEqual(objpath["Antecedent"], refs[0])
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestHostedSoftwareIdentityResource)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_installed_software_identity.py b/src/software/test/test_installed_software_identity.py
new file mode 100755
index 0000000..ade841b
--- /dev/null
+++ b/src/software/test/test_installed_software_identity.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_InstalledSoftwareIdentity provider.
+"""
+
+import pywbem
+import socket
+import unittest
+
+import base
+import rpmcache
+
+class TestInstalledSoftwareIdentity(base.SoftwareBaseTestCase):
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_InstalledSoftwareIdentity"
+ KEYS = ("InstalledSoftware", "System")
+
+ def make_op(self, pkg, newer=True):
+ """
+ @return object path of InstalledSoftwareIdentity association
+ """
+ objpath = self.objpath.copy()
+ objpath["System"] = pywbem.CIMInstanceName(
+ classname="Linux_ComputerSystem",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "CreationClassName" : "Linux_ComputerSystem",
+ "Name" : socket.gethostname()
+ }))
+ objpath["InstalledSoftware"] = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "InstanceID" : 'LMI:SoftwareIdentity:' + pkg.get_nevra(newer=newer,
+ with_epoch="ALWAYS")
+ }))
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ for pkg in self.safe_pkgs:
+ objpath = self.make_op(pkg)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(inst.path, objpath,
+ "Object paths should match for package %s"%pkg)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ "OP is missing \"%s\" key for package %s"%(key, pkg))
+ self.assertEqual(inst[key], inst.path[key])
+ self.assertEqual(objpath, inst.path,
+ "Object paths should match for package %s"%pkg)
+
+ # try it now with CIM_ComputerSystem
+ system = objpath["System"].copy()
+ objpath["System"].classname = "CIM_ComputerSystem"
+ objpath["System"]["CreationClassName"] = "CIM_ComputerSystem"
+
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(objpath, inst.path,
+ "Object paths should match for package %s"%pkg)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ "OP is missing \"%s\" key for package %s"%(key, pkg))
+ self.assertEqual(system, inst["System"])
+ self.assertEqual(objpath, inst.path)
+
+ @base.mark_tedious
+ def test_enum_instance_names_safe(self):
+ """
+ Tests EnumInstanceNames call on installed packages.
+ """
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertGreater(len(inames), 0)
+ objpath = self.make_op(self.safe_pkgs[0])
+ for iname in inames:
+ self.assertIsInstance(iname, pywbem.CIMInstanceName)
+ self.assertEqual(iname.namespace, 'root/cimv2')
+ self.assertEqual(iname.classname, self.CLASS_NAME)
+ self.assertEqual(sorted(iname.keys()), sorted(self.KEYS))
+ self.assertEqual(objpath["System"], iname["System"])
+ nevra_set = set(i["InstalledSoftware"]["InstanceID"] for i in inames)
+ for pkg in self.safe_pkgs:
+ nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS")
+ self.assertTrue(nevra in nevra_set,
+ 'Missing nevra "%s".' % nevra)
+
+ @base.mark_tedious
+ def test_enum_instances(self):
+ """
+ Tests EnumInstances call on installed packages.
+ """
+ insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ self.assertGreater(len(insts), 0)
+ for inst in insts:
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ self.assertEqual(inst.path.namespace, 'root/cimv2')
+ self.assertEqual(inst.path.classname, self.CLASS_NAME)
+ self.assertEqual(inst.classname, self.CLASS_NAME)
+ self.assertEqual(sorted(inst.keys()), sorted(self.KEYS))
+ self.assertEqual(sorted(inst.path.keys()), sorted(self.KEYS))
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+ nevra_set = set(i["InstalledSoftware"]["InstanceID"] for i in insts)
+ for pkg in self.safe_pkgs:
+ nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS")
+ self.assertTrue(nevra in nevra_set, "Missing pkg %s in nevra_set."
+ % nevra)
+
+ @base.mark_tedious
+ @base.mark_dangerous
+ def test_enum_instance_names(self):
+ """
+ Tests EnumInstanceNames call on dangerous packages.
+ """
+ pkg = self.dangerous_pkgs[0]
+
+ rpmcache.ensure_pkg_installed(pkg)
+
+ inames1 = self.conn.EnumerateInstanceNames(
+ ClassName=self.CLASS_NAME)
+ self.assertGreater(len(inames1), 1)
+ self.assertIn('LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS"),
+ set(i["InstalledSoftware"]["InstanceID"] for i in inames1))
+
+ rpmcache.remove_pkg(pkg.name)
+ inames2 = self.conn.EnumerateInstanceNames(
+ ClassName=self.CLASS_NAME)
+ self.assertEqual(len(inames1), len(inames2) + 1)
+ self.assertNotIn('LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS"),
+ set(i["InstalledSoftware"]["InstanceID"] for i in inames2))
+
+ rpmcache.install_pkg(pkg)
+ inames3 = self.conn.EnumerateInstanceNames(
+ ClassName=self.CLASS_NAME)
+ self.assertEqual(len(inames1), len(inames3))
+ self.assertIn('LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS"),
+ set(i["InstalledSoftware"]["InstanceID"] for i in inames3))
+
+ @base.mark_dangerous
+ def test_create_instance(self):
+ """
+ Tests new instance creation for dangerous packages.
+ """
+ for pkg in self.dangerous_pkgs:
+ if rpmcache.is_pkg_installed(pkg.name):
+ rpmcache.remove_pkg(pkg.name)
+ objpath = self.make_op(pkg)
+ self.assertFalse(rpmcache.is_pkg_installed(pkg))
+
+ inst = pywbem.CIMInstance(self.CLASS_NAME,
+ properties={
+ "InstalledSoftware" : objpath["InstalledSoftware"],
+ "System" : objpath["System"]},
+ path=objpath)
+ iname = self.conn.CreateInstance(NewInstance=inst)
+ self.assertTrue(rpmcache.is_pkg_installed(pkg))
+ self.assertEqual(objpath, iname,
+ 'Object path does not match for %s.' % pkg)
+
+ # try to install second time
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.CreateInstance(NewInstance=inst)
+ self.assertEqual(cm.exception.args[0],
+ pywbem.CIM_ERR_ALREADY_EXISTS)
+
+ @base.mark_dangerous
+ def test_delete_instance(self):
+ """
+ Tests removing of dangerous packages by deleting instance name.
+ """
+ for pkg in self.dangerous_pkgs:
+ self.ensure_pkg_installed(pkg)
+ self.assertTrue(rpmcache.is_pkg_installed(pkg))
+ objpath = self.make_op(pkg)
+ self.conn.DeleteInstance(InstanceName=objpath)
+ self.assertFalse(rpmcache.is_pkg_installed(pkg),
+ "Failed to delete instance for %s." % pkg)
+
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.DeleteInstance(InstanceName=objpath)
+ self.assertEqual(cm.exception.args[0], pywbem.CIM_ERR_NOT_FOUND)
+
+ @base.mark_tedious
+ def test_get_system_referents(self):
+ """
+ Test ReferenceNames for ComputerSystem.
+ """
+ objpath = self.make_op(self.safe_pkgs[0])
+ refs = self.conn.AssociatorNames(
+ Role="System",
+ ObjectName=objpath["System"],
+ ResultRole="InstalledSoftware",
+ ResultClass="LMI_SoftwareIdentity")
+ self.assertGreater(len(refs), 0)
+ for ref in refs:
+ self.assertIsInstance(ref, pywbem.CIMInstanceName)
+ self.assertEqual(ref.namespace, 'root/cimv2')
+ self.assertEqual(ref.classname, "LMI_SoftwareIdentity")
+ self.assertEqual(sorted(ref.keys()), ["InstanceID"])
+ self.assertTrue(ref["InstanceID"].startswith("LMI:SoftwareIdentity:"))
+
+ nevra_set = set(i["InstanceID"] for i in refs)
+ for pkg in self.safe_pkgs:
+ nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS")
+ self.assertTrue(nevra in nevra_set,
+ 'Missing nevra "%s".' % nevra)
+
+ def test_get_installed_software_referents(self):
+ """
+ Test ReferenceNames for SoftwareIdentity.
+ """
+ for pkg in self.safe_pkgs:
+ objpath = self.make_op(pkg)
+ refs = self.conn.AssociatorNames(
+ ObjectName=objpath["InstalledSoftware"],
+ Role="InstalledSoftware",
+ ResultRole="System",
+ ResultClass="Linux_ComputerSystem")
+ self.assertEqual(len(refs), 1)
+ ref = refs[0]
+ self.assertEqual(objpath["System"], ref)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestInstalledSoftwareIdentity)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_member_of_software_collection.py b/src/software/test/test_member_of_software_collection.py
new file mode 100755
index 0000000..0f6bac8
--- /dev/null
+++ b/src/software/test/test_member_of_software_collection.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_MemberOfSoftwareCollection provider.
+"""
+
+import pywbem
+import unittest
+
+import base
+
+class TestMemberOfSoftwareCollection(base.SoftwareBaseTestCase):
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_MemberOfSoftwareCollection"
+ KEYS = ("Collection", "Member")
+
+ def make_op(self, pkg, newer=True):
+ """
+ @return object path of MembeOfSoftwareCollection association
+ """
+ objpath = self.objpath.copy()
+ objpath["Collection"] = pywbem.CIMInstanceName(
+ classname="LMI_SystemSoftwareCollection",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "InstanceID" : "LMI:SystemSoftwareCollection"
+ }))
+ objpath["Member"] = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "InstanceID" : 'LMI:SoftwareIdentity:' + pkg.get_nevra(newer=newer,
+ with_epoch="ALWAYS")
+ }))
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ for pkg in self.safe_pkgs:
+ objpath = self.make_op(pkg)
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertEqual(inst.path, objpath,
+ "Object paths should match for package %s"%pkg)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ "OP is missing \"%s\" key for package %s"%(key, pkg))
+ self.assertEqual(objpath, inst.path)
+
+# @base.mark_tedious
+# def test_enum_instance_names(self):
+# """
+# Tests EnumInstanceNames call on installed packages.
+# """
+# inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+# self.assertGreater(len(inames), 0)
+# objpath = self.make_op(self.safe_pkgs[0])
+# for iname in inames:
+# self.assertIsInstance(iname, pywbem.CIMInstanceName)
+# self.assertEqual(iname.namespace, 'root/cimv2')
+# self.assertEqual(iname.classname, self.CLASS_NAME)
+# self.assertEqual(sorted(iname.keys()), sorted(self.KEYS))
+# self.assertEqual(objpath["Collection"], iname["Collection"])
+# nevra_set = set(i["Member"]["InstanceID"] for i in inames)
+# for pkg in self.safe_pkgs:
+# nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS")
+# self.assertTrue(nevra in nevra_set,
+# 'Missing nevra "%s".' % nevra)
+
+# @base.mark_tedious
+# def test_enum_instances(self):
+# """
+# Tests EnumInstances call on installed packages.
+# """
+# insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+# self.assertGreater(len(insts), 0)
+# for inst in insts:
+# self.assertIsInstance(inst, pywbem.CIMInstance)
+# self.assertEqual(inst.namespace, 'root/cimv2')
+# self.assertEqual(inst.classname, self.CLASS_NAME)
+# self.assertEqual(sorted(inst.keys()), sorted(self.KEYS))
+# self.assertEqual(inst["InstanceID"], inst.path["InstanceID"])
+# nevra_set = set(i["Member"]["InstanceID"] for i in insts)
+# for pkg in self.safe_pkgs:
+# self.assertIn(pkg.get_nevra(with_epoch="ALWAYS"), nevra_set)
+
+# @base.mark_tedious
+# def test_get_collection_referents(self):
+# """
+# Test ReferenceNames for SystemSoftwareCollection.
+# """
+# objpath = self.make_op(self.safe_pkgs[0])
+# refs = self.conn.AssociatorNames(
+# Role="Collection",
+# ObjectName=objpath["Collection"],
+# ResultRole="Member",
+# ResultClass="LMI_SoftwareIdentity")
+# self.assertGreater(len(refs), 0)
+# for ref in refs:
+# self.assertIsInstance(ref, pywbem.CIMInstanceName)
+# self.assertEqual(ref.namespace, 'root/cimv2')
+# self.assertEqual(ref.classname, "LMI_SoftwareIdentity")
+# self.assertEqual(sorted(ref.keys()), ["InstanceID"])
+# self.assertTrue(ref["InstanceID"].startswith("LMI:SoftwareIdentity:"))
+# nevra_set = set(i["InstanceID"] for i in refs)
+# # NOTE: installed packages might not be available
+# for pkg in self.dangerous_pkgs:
+# nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS")
+# self.assertTrue(nevra in nevra_set,
+# 'Missing nevra "%s".' % nevra)
+#
+ def test_get_member_referents(self):
+ """
+ Test ReferenceNames for SoftwareIdentity.
+ """
+ for pkg in self.safe_pkgs:
+ objpath = self.make_op(pkg)
+ refs = self.conn.AssociatorNames(
+ ObjectName=objpath["Member"],
+ Role="Member",
+ ResultRole="Collection",
+ ResultClass="LMI_SystemSoftwareCollection")
+ self.assertEqual(len(refs), 1)
+ ref = refs[0]
+ self.assertEqual(objpath["Collection"], ref)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestMemberOfSoftwareCollection)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_resource_for_software_identity.py b/src/software/test/test_resource_for_software_identity.py
new file mode 100755
index 0000000..4c7e6b8
--- /dev/null
+++ b/src/software/test/test_resource_for_software_identity.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_ResourceForSoftwareIdentity provider.
+"""
+
+import pywbem
+import socket
+import unittest
+
+import base
+
+class TestResourceForSoftwareIdentity(base.SoftwareBaseTestCase):
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_ResourceForSoftwareIdentity"
+ KEYS = ("ManagedElement", "AvailableSAP")
+
+ @classmethod
+ def needs_repodb(cls):
+ return True
+
+ def make_op(self, pkg=None, newer=True, repo=None):
+ """
+ @return object path of ResourceForSoftwareIdentity association
+ """
+ objpath = self.objpath.copy()
+ objpath["AvailableSAP"] = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentityResource",
+ namespace="root/cimv2",
+ keybindings=pywbem.NocaseDict({
+ "CreationClassName" : "LMI_SoftwareIdentityResource",
+ "SystemCreationClassName" : "Linux_ComputerSystem",
+ "SystemName" : socket.gethostname()
+ }))
+ if repo is not None:
+ objpath["AvailableSAP"]["Name"] = repo.repoid
+ elif pkg is not None:
+ objpath["AvailableSAP"]["Name"] = getattr(pkg,
+ 'up_repo' if newer else 'repo')
+ objpath["ManagedElement"] = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ if pkg is not None:
+ objpath["ManagedElement"]["InstanceID"] = \
+ 'LMI:SoftwareIdentity:' + pkg.get_nevra(newer=newer, with_epoch="ALWAYS")
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ for pkg in self.dangerous_pkgs:
+ objpath = self.make_op(pkg)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(objpath, inst.path,
+ "Object paths should match for package %s"%pkg)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ 'OP is missing \"%s\" key for package "%s".' % (key, pkg))
+ self.assertEqual(inst[key], inst.path[key],
+ 'Key property "%s" does not match for package "%s"!' %
+ (key, pkg))
+
+ @base.mark_tedious
+ def test_get_resource_referents(self):
+ """
+ Test ReferenceNames for AvailableSAP.
+ """
+ for repo in self.repodb:
+ objpath = self.make_op(repo=repo)
+ if not repo.pkg_count or repo.pkg_count > 10000:
+ # do not test too big repositories
+ continue
+ refs = self.conn.AssociatorNames(
+ Role="AvailableSAP",
+ ObjectName=objpath["AvailableSAP"],
+ ResultRole="ManagedElement",
+ ResultClass="LMI_SoftwareIdentity")
+ # there may be empty repositories
+ #self.assertGreater(len(refs), 0)
+ for ref in refs:
+ self.assertIsInstance(ref, pywbem.CIMInstanceName)
+ self.assertEqual(ref.namespace, 'root/cimv2')
+ self.assertEqual(ref.classname, "LMI_SoftwareIdentity")
+ self.assertEqual(sorted(ref.keys()), ["InstanceID"])
+ self.assertTrue(ref["InstanceID"].startswith("LMI:SoftwareIdentity:"))
+
+ nevra_set = set(i["InstanceID"] for i in refs)
+ # NOTE: installed packages might not be available
+ for pkg, up in ((pkg, up) for pkg in self.dangerous_pkgs
+ for up in (True, False)):
+ nevra = 'LMI:SoftwareIdentity:'+pkg.get_nevra(
+ newer=up, with_epoch="ALWAYS")
+ reponame = getattr(pkg, 'up_repo' if up else 'repo')
+ if reponame == repo.repoid:
+ self.assertTrue(nevra in nevra_set,
+ 'Missing nevra "%s" for repo "%s".' % (nevra,
+ reponame))
+
+ @base.mark_tedious
+ def test_get_resource_referents_for_disabled_repo(self):
+ """
+ Test ReferenceNames for AvailableSAP, which is disabled.
+ """
+ for repo in self.repodb:
+ if repo.status:
+ continue # test only disabled repositories
+ objpath = self.make_op(repo=repo)
+ refs = self.conn.AssociatorNames(
+ Role="AvailableSAP",
+ ObjectName=objpath["AvailableSAP"],
+ ResultRole="ManagedElement",
+ ResultClass="LMI_SoftwareIdentity")
+ self.assertGreater(len(refs), 0)
+ for ref in refs:
+ self.assertIsInstance(ref, pywbem.CIMInstanceName)
+ self.assertEqual(ref.namespace, 'root/cimv2')
+ self.assertEqual(ref.classname, "LMI_SoftwareIdentity")
+ self.assertEqual(sorted(ref.keys()), ["InstanceID"])
+ self.assertTrue(ref["InstanceID"].startswith("LMI:SoftwareIdentity:"))
+
+ def test_get_managed_element_referents(self):
+ """
+ Test ReferenceNames for SoftwareIdentity.
+ """
+ for pkg, up in ((pkg, up) for pkg in self.dangerous_pkgs
+ for up in (True, False)):
+ objpath = self.make_op(pkg, newer=up)
+ refs = self.conn.AssociatorNames(
+ ObjectName=objpath["ManagedElement"],
+ Role="ManagedElement",
+ ResultRole="AvailableSAP",
+ ResultClass="LMI_SoftwareIdentityResource")
+ self.assertEqual(1, len(refs),
+ 'No repo found for pkg "%s".' % pkg.get_nevra(newer=up,
+ with_epoch="ALWAYS"))
+ ref = refs[0]
+ self.assertIsInstance(ref, pywbem.CIMInstanceName)
+ self.assertEqual(ref.namespace, 'root/cimv2')
+ self.assertEqual(ref.classname, "LMI_SoftwareIdentityResource")
+ self.assertEqual(sorted(ref.keys()),
+ sorted(["SystemCreationClassName", "SystemName",
+ "Name", "CreationClassName"]))
+ self.assertEqual(
+ getattr(pkg, 'up_repo' if up else 'repo'),
+ ref["Name"], 'Repository name does not match for pkg "%s"'%
+ pkg.get_nevra(newer=up, with_epoch="ALWAYS"))
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestResourceForSoftwareIdentity)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_software_file_check.py b/src/software/test/test_software_file_check.py
deleted file mode 100755
index d41f052..0000000
--- a/src/software/test/test_software_file_check.py
+++ /dev/null
@@ -1,527 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-"""
-Unit tests for LMI_SoftwareFileCheck provider.
-"""
-
-import hashlib
-import os
-import pywbem
-import re
-import subprocess
-import unittest
-from collections import namedtuple
-
-import common
-
-RE_CHECKSUM = re.compile(r'^([0-9a-fA-F]+)\s+.*')
-
-PassedFlags = namedtuple("PassedFlags", #pylint: disable=C0103
- "exists, type, size, mode, checksum, dev, ltarget, uid, gid, mtime")
-
-class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
- #pylint: disable=R0904
- """
- Basic cim operations test.
- """
-
- CLASS_NAME = "LMI_SoftwareFileCheck"
- KEYS = ( "CheckID", "Name", "SoftwareElementID", "SoftwareElementState"
- , "TargetOperatingSystem", "Version")
-
- hash_num2algo = {
- 1 : hashlib.md5, #pylint: disable=E1101
- 2 : hashlib.sha1, #pylint: disable=E1101
- 8 : hashlib.sha256, #pylint: disable=E1101
- 9 : hashlib.sha384, #pylint: disable=E1101
- 10 : hashlib.sha512, #pylint: disable=E1101
- 11 : hashlib.sha224 #pylint: disable=E1101
- }
-
- hash_num2cmd = {
- 1 : "md5sum",
- 2 : "sha1sum",
- 8 : "sha256sum",
- 9 : "sha384sum",
- 10 : "sha512sum",
- 11 : "sha224sum"
- }
-
- hash_num2length = {
- 1 : 32,
- 2 : 40,
- 8 : 64,
- 9 : 96,
- 10 : 128,
- 11 : 56
- }
-
- def make_op(self, pkg, filename, newer=True):
- """
- @return object path of LMI_SoftwareFileCheck
- """
- objpath = self.objpath.copy()
- objpath["Name"] = filename
- objpath["Version"] = getattr(pkg, "up_ver" if newer else "ver")
- objpath["CheckID"] = "%s#%s" % (pkg.name, filename)
- objpath["SoftwareElementState"] = pywbem.Uint16(2)
- objpath["TargetOperatingSystem"] = pywbem.Uint16(36)
- objpath["SoftwareElementID"] = pkg.get_nevra(newer)
- return objpath
-
- def assertEqualSEID(self, id1, id2, msg=None):
- """
- This is for comparison of SoftwareElementID property values.
- """
- if ( not isinstance(id1, basestring)
- or not isinstance(id2, basestring)):
- return common.SoftwareBaseTestCase.assertEqual(self, id1, id2, msg)
- match1 = common.RE_NEVRA.match(id1)
- match2 = common.RE_NEVRA.match(id2)
- if not match1 or not match2:
- return common.SoftwareBaseTestCase.assertEqual(self, id1, id2, msg)
- if any( match1.group(g) != match2.group(g)
- for g in ('name', 'ver', 'rel', 'arch')):
- return common.SoftwareBaseTestCase.assertEqual(self, id1, id2, msg)
- epoch1 = match1.group('epoch')
- epoch2 = match2.group('epoch')
- if not epoch1:
- epoch1 = '0'
- if not epoch2:
- epoch2 = '0'
- if epoch1 != epoch2:
- return common.SoftwareBaseTestCase.assertEqual(self, id1, id2, msg)
- return True
-
- def assertEqual(self, op1, op2, msg=None):
- """
- This is override for object paths, that allows some differences
- (like missing epoch in SoftwareElementID).
- """
- if ( not isinstance(op1, pywbem.CIMInstanceName)
- or not isinstance(op2, pywbem.CIMInstanceName)
- or op1.classname != op2.classname
- or op1.namespace != op2.namespace
- or op1.keybindings.keys() != op2.keybindings.keys()
- or any(op1[a] != op2[a] for a in
- ( 'Name', 'Version', 'CheckID', 'SoftwareElementState'
- , 'TargetOperatingSystem'))):
- return common.SoftwareBaseTestCase.assertEqual(self, op1, op2, msg)
- return self.assertEqualSEID(
- op1['SoftwareElementID'], op2['SoftwareElementID'], msg)
-
- def make_checksum_str(self, csumnum, filename):
- """
- @return checksum of installed file
- """
- return RE_CHECKSUM.match(subprocess.check_output([
- self.hash_num2cmd[csumnum], filename])).group(1).lower()
-
- def do_check_symlink(self, pkg, filepath, inst):
- """
- Assert some details about symlink.
- """
- target = os.readlink(filepath)
- stats = os.lstat(filepath)
-
- self.assertEqual(pywbem.Uint16(3), inst["FileType"],
- "Unexpected file type of symlink for %s:%s"%(
- pkg.name, filepath))
- self.assertEqual(stats.st_uid, inst["FileUserID"],
- "Unexpected uid of symlink for %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_gid, inst["FileGroupID"],
- "Unexpected gid of symlink for %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_mode, inst["FileMode"],
- "Unexpected mode of symlink for %s:%s" %(pkg.name, filepath))
- self.assertEqual(stats.st_size, inst["FileSize"],
- "Unexpected size of symlink for %s:%s"%(pkg.name, filepath))
- self.assertEqual(target, inst["LinkTarget"],
- "Unexpected size of symlink for %s:%s"%(pkg.name, filepath))
- self.assertEqual("0"*self.hash_num2length[inst["FileChecksumType"]],
- inst["FileChecksum"],
- "Unexpected hash of symlink for %s:%s"%(pkg.name, filepath))
- self.assertEqual(int(stats.st_mtime), inst["LastModificationTime"],
- "Unexpected mtime of symlink for %s:%s"%(pkg.name, filepath))
-
- # modify owner
- prev_user = inst["FileUserID"]
- #prev_mtime = inst["LastModificationTime"]
- #prev_pflags = PassedFlags(*inst["PassedFlags"])
- os.lchown(filepath, stats.st_uid + 1, -1)
- inst = self.conn.GetInstance(InstanceName=inst.path)
- self.assertEqual(inst["ExpectedFileUserID"] + 1, inst["FileUserID"],
- "Unexpected uid of modified symlink for %s:%s"%(
- pkg.name, filepath))
- self.assertEqual(prev_user + 1, inst["FileUserID"],
- "Unexpected uid of modified symlink for %s:%s"%(
- pkg.name, filepath))
- self.assertEqual(stats.st_gid, inst["FileGroupID"],
- "Unexpected gid of modified symlink for %s:%s"%(
- pkg.name, filepath))
- cur_pflags = PassedFlags(*inst["PassedFlags"])
- #self.assertGreater(inst["LastModificationTime"], prev_mtime)
-
- self.assertTrue(cur_pflags.exists,
- "Symlink %s:%s should exist." %(pkg.name, filepath))
- self.assertTrue(cur_pflags.type,
- "File type should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.size,
- "File size should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.mode,
- "File mode should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.checksum,
- "File checksum should match for symlink %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.dev,
- "Device number should match for symlink %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.ltarget,
- "Link target should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.uid,
- "Uid should not match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.gid,
- "Gid shoud match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.mtime,
- "Mtime should match for symlink %s:%s"%(pkg.name, filepath))
-
- # modify link_target
- os.remove(filepath)
- os.symlink("wrong" + "*"*len(inst["ExpectedLinkTarget"]), filepath)
- os.lchown(filepath, inst["ExpectedFileUserID"],
- inst["ExpectedFileGroupID"])
-
- inst = self.conn.GetInstance(InstanceName=inst.path)
- cur_pflags = PassedFlags(*inst["PassedFlags"])
- self.assertGreater(len(inst["LinkTarget"]),
- len(inst["ExpectedLinkTarget"]))
-
- self.assertTrue(cur_pflags.exists,
- "File %s:%s should exist"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.type,
- "File type should match for symlink %s:%s"%(pkg.name, filepath))
- # file size not checked for symlinks
- #self.assertFalse(cur_pflags.size,
- #"File size should not match for symlink %s:%s"%(
- #pkg.name, filepath))
- self.assertTrue(cur_pflags.mode,
- "File mode should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.checksum,
- "Checksum should match for symlink %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.dev,
- "Device numbers should match for symlink %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.ltarget,
- "Link target should not match for symlink %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.uid,
- "File uid should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.gid,
- "File gid should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.mtime,
- "File mtime should match for symlink %s:%s"%(pkg.name, filepath))
-
- def do_check_directory(self, pkg, filepath, inst):
- """
- Assert some details about directory.
- """
- stats = os.lstat(filepath)
-
- self.assertEqual(pywbem.Uint16(2), inst["FileType"],
- "Unexpected type for directory %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_uid, inst["FileUserID"],
- "Unexpected uid for directory %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_gid, inst["FileGroupID"],
- "Unexpected gid for directory %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_mode, inst["FileMode"],
- "Unexpected mode for directory %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_size, inst["FileSize"],
- "Unexpected size for directory %s:%s"%(pkg.name, filepath))
- self.assertIs(inst["LinkTarget"], None)
- self.assertEqual("0"*self.hash_num2length[inst["FileChecksumType"]],
- inst["FileChecksum"],
- "Unexpected checksum for directory %s:%s"%(pkg.name, filepath))
- self.assertEqual(int(stats.st_mtime), inst["LastModificationTime"],
- "Unexpected mtime for directory %s:%s"%(pkg.name, filepath))
-
- def do_check_file(self, pkg, filepath, inst):
- """
- Assert some details about regurar file.
- """
- stats = os.lstat(filepath)
-
- self.assertEqual(pywbem.Uint16(1), inst["FileType"],
- "Unexpected file type for %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_uid, inst["FileUserID"],
- "Unexpected file uid for %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_gid, inst["FileGroupID"],
- "Unexpected gid for regular file %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_mode, inst["FileMode"],
- "Unexpected mode for reqular file %s:%s"%(pkg.name, filepath))
- self.assertEqual(stats.st_size, inst["FileSize"],
- "Unexpected size for reqular file %s:%s"%(pkg.name, filepath))
- self.assertIs(inst["LinkTarget"], None)
- csum = self.make_checksum_str(inst['FileChecksumType'], filepath)
- self.assertEqual(csum, inst["FileChecksum"].lower(),
- "Unexpected checksum for reqular file %s:%s"%(pkg.name, filepath))
- self.assertEqual(inst["LastModificationTime"],
- inst["ExpectedLastModificationTime"],
- "Unexpected mtime for reqular file %s:%s"%(pkg.name, filepath))
- self.assertEqual(int(stats.st_mtime), inst["LastModificationTime"],
- "Unexpected mtime for reqular file %s:%s"%(pkg.name, filepath))
-
- # make it longer
- with open(filepath, "a+") as fobj:
- fobj.write("data\n")
- inst = self.conn.GetInstance(InstanceName=inst.path)
- cur_pflags = PassedFlags(*inst["PassedFlags"])
- self.assertGreater(inst["FileSize"], inst["ExpectedFileSize"],
- "File size should be greater, then expected for reqular file"
- " %s:%s"%(pkg.name, filepath))
- self.assertGreater(inst["LastModificationTime"],
- inst["ExpectedLastModificationTime"],
- "Unexpected mtime for reqular file %s:%s"%(pkg.name, filepath))
-
- self.assertTrue(cur_pflags.exists,
- "Regular file should exist %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.type,
- "Type of regular file should match for %s:%s"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.size,
- "Size should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.mode,
- "Mode should match for regular file %s:%s"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.checksum,
- "Checksum should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.dev,
- "Device number should match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.ltarget,
- "Link target should match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.uid,
- "File uid should match for %s:%s"%(pkg.name, filepath))
- self.assertTrue(cur_pflags.gid,
- "File gid should match for %s:%s"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.mtime,
- "File mtime should not match for %s:%s"%(pkg.name, filepath))
-
- # change file type
- os.remove(filepath)
- os.symlink(filepath, filepath)
- os.lchown(filepath, inst["ExpectedFileUserID"],
- inst["ExpectedFileGroupID"])
- inst = self.conn.GetInstance(InstanceName=inst.path)
- cur_pflags = PassedFlags(*inst["PassedFlags"])
- self.assertNotEqual(inst["ExpectedLinkTarget"], inst["LinkTarget"],
- "Link target should not match for %s:%s"%(pkg.name, filepath))
- self.assertNotEqual(inst["ExpectedFileSize"], inst["FileSize"],
- "File size should not match for %s:%s"%(pkg.name, filepath))
- self.assertGreater(inst["LastModificationTime"],
- inst["ExpectedLastModificationTime"],
- "File mtime should be greater than expected for %s:%s"%(
- pkg.name, filepath))
- self.assertNotEqual(inst["ExpectedFileType"], inst["FileType"],
- "File type should not match for %s:%s"%(pkg.name, filepath))
- self.assertEqual(pywbem.Uint16(3), inst["FileType"],
- "File type should match for %s:%s"%(pkg.name, filepath))
-
- self.assertTrue(cur_pflags.exists,
- "Regular file %s:%s should exist"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.type,
- "Regular file type should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.size,
- "Size should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.mode,
- "File mode should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.checksum,
- "Checksum should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.dev,
- "Device should match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.ltarget,
- "Link target should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.uid,
- "Regular file's uid should match for %s:%s"%(
- pkg.name, filepath))
- self.assertTrue(cur_pflags.gid,
- "Regular file's gid should match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.mtime,
- "Regular file's mtime should not match for %s:%s"%(
- pkg.name, filepath))
-
- # remove it
- os.remove(filepath)
- inst = self.conn.GetInstance(InstanceName=inst.path)
- cur_pflags = PassedFlags(*inst["PassedFlags"])
- self.assertEqual(inst["ExpectedLinkTarget"], inst["LinkTarget"],
- "Link target does not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertNotEqual(inst["ExpectedFileSize"], inst["FileSize"],
- "File size should not match for regular file %s:%s"%(
- pkg.name, filepath))
- self.assertIsNone(inst["LastModificationTime"])
- self.assertIsNone(inst["FileType"])
- self.assertIsNone(inst["FileChecksum"])
- self.assertIsNone(inst["FileMode"])
- self.assertIsNone(inst["FileUserID"])
- self.assertIsNone(inst["FileGroupID"])
-
- self.assertFalse(cur_pflags.exists,
- "Regular file %s:%s should not exist"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.type,
- "Regular file's type should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.size,
- "Regular file's size should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.mode,
- "Regular file's mode should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.checksum,
- "Regular file's checksum should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.dev,
- "Regular file's dev number should not match %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.ltarget,
- "Regular file's link target should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.uid,
- "Regular file's uid should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.gid,
- "Regular file's guid should not match for %s:%s"%(
- pkg.name, filepath))
- self.assertFalse(cur_pflags.mtime,
- "Regular file's mtime should not match for %s:%s"%(
- pkg.name, filepath))
-
- @common.mark_dangerous
- def test_get_instance(self):
- """
- Tests GetInstance call.
- """
- for pkg in self.pkgdb:
- files = self.pkg_files[pkg.name]
- if ( common.is_installed(pkg)
- and not common.verify_pkg(pkg.name)):
- common.remove_pkg(pkg.name)
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg)
- self.assertTrue(common.is_installed(pkg),
- "Package %s must be installed"%pkg)
-
- for filepath in files:
- objpath = self.make_op(pkg, filepath)
-
- inst = self.conn.GetInstance(
- InstanceName=objpath,
- LocalOnly=False)
- self.assertIsInstance(inst, pywbem.CIMInstance)
- self.assertEqual(objpath, inst.path,
- msg="Object paths of instance must match for %s:%s"%(
- pkg.name, filepath))
- for key in self.KEYS:
- if key.lower() == "softwareelementid":
- self.assertEqualSEID(inst[key], objpath[key],
- "OP key %s values should match for %s:%s"%(
- key, pkg.name, filepath))
- elif key.lower() == "targetoperatingsystem":
- self.assertIsInstance(objpath[key], (int, long))
- else:
- self.assertEqual(objpath[key], inst[key],
- "OP key %s values should match for %s:%s"%(
- key, pkg.name, filepath))
-
- self.assertTrue(inst["FileExists"],
- "File %s:%s must exist"%(pkg.name, filepath))
- self.assertEqual(10, len(inst["PassedFlags"]),
- "PassedFlags must have constant length")
- for i, flag in enumerate(inst["PassedFlags"]):
- self.assertTrue(flag is True,
- "Flag \"%s\" should all match for file %s:%s"%(
- inst["PassedFlagsDescriptions"][i], pkg.name, filepath))
- for prop in ( "FileType", "FileUserID", "FileGroupID"
- , "FileMode", "FileSize", "LinkTarget"
- , "FileChecksum", "FileModeFlags"):
- if ( ( os.path.islink(filepath)
- or (not os.path.isfile(filepath)))
- and prop == "FileSize"):
- continue
- self.assertEqual(inst["Expected"+prop], inst[prop],
- "%s should match for %s:%s"%(prop, pkg.name, filepath))
- if os.path.islink(filepath):
- self.do_check_symlink(pkg, filepath, inst)
- elif os.path.isdir(filepath):
- self.do_check_directory(pkg, filepath, inst)
- elif os.path.isfile(filepath):
- self.do_check_file(pkg, filepath, inst)
-
- @common.mark_dangerous
- def test_invoke_method(self):
- """
- Tests Invoke method invocation.
- """
- for pkg in self.pkgdb:
- files = self.pkg_files[pkg.name]
- if common.is_installed(pkg) and not common.verify_pkg(pkg.name):
- common.remove_pkg(pkg.name)
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg)
- self.assertTrue(common.is_installed(pkg),
- "Package %s must be installed"%pkg)
- for filepath in files:
- objpath = self.make_op(pkg, filepath)
-
- (rval, _) = self.conn.InvokeMethod(
- MethodName="Invoke",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(0), rval,
- msg="InvokeMethod should be successful for %s:%s"%(
- pkg.name, filepath))
-
- # modify file
- if os.path.isfile(filepath):
- os.remove(filepath)
- else:
- os.lchown(filepath, os.stat(filepath).st_uid + 1, -1)
- (rval, _) = self.conn.InvokeMethod(
- MethodName="Invoke",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(2), rval,
- "InvokeMethod should not pass for modified file %s:%s"%(
- pkg.name, filepath))
-
-def suite():
- """For unittest loaders."""
- return unittest.TestLoader().loadTestsFromTestCase(
- TestSoftwareFileCheck)
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/software/test/test_software_identity.py b/src/software/test/test_software_identity.py
new file mode 100755
index 0000000..cf92bab
--- /dev/null
+++ b/src/software/test/test_software_identity.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_SoftwareIdentity provider.
+"""
+
+from datetime import datetime, timedelta
+import itertools
+import pywbem
+import unittest
+
+import base
+import rpmcache
+import util
+
+class TestSoftwareIdentity(base.SoftwareBaseTestCase): #pylint: disable=R0904
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_SoftwareIdentity"
+ KEYS = ("InstanceID", )
+
+ def make_op(self, pkg, newer=True):
+ """
+ @param ses SoftwareElementState property value
+ @return object path of SoftwareIdentity
+ """
+ objpath = self.objpath.copy()
+ objpath["InstanceID"] = 'LMI:SoftwareIdentity:'+pkg.get_nevra(newer, "ALWAYS")
+ return objpath
+
+ @base.mark_dangerous
+ def test_get_instance(self):
+ """
+ Dangerous test, making sure, that properties are set correctly
+ for installed and removed packages.
+ """
+ for pkg in self.dangerous_pkgs:
+ if rpmcache.is_pkg_installed(pkg.name):
+ rpmcache.remove_pkg(pkg.name)
+ objpath = self.make_op(pkg)
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertIsNone(inst["InstallDate"])
+ time_stamp = datetime.now(pywbem.cim_types.MinutesFromUTC(0)) \
+ - timedelta(seconds=0.01)
+ rpmcache.install_pkg(pkg)
+ inst2 = self.conn.GetInstance(
+ InstanceName=objpath, LocalOnly=False)
+ self.assertIsNotNone(inst2["InstallDate"])
+ self.assertGreater(inst2["InstallDate"].datetime, time_stamp)
+
+ def test_get_instance_safe(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ for pkg in self.safe_pkgs:
+ objpath = self.make_op(pkg)
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+ self.assertEqual(inst.path, objpath,
+ "Object paths should match for package %s"%pkg)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key),
+ "OP is missing \"%s\" key for package %s"%(key, pkg))
+ self.assertIsInstance(inst["Caption"], basestring)
+ self.assertIsInstance(inst["Description"], basestring)
+ self.assertEqual(pkg.up_evra, inst["VersionString"],
+ "VersionString does not match evra for pkg %s" % pkg)
+ self.assertTrue(inst['IsEntity'])
+ self.assertEqual(pkg.name, inst["Name"],
+ "Name does not match for pkg %s" % pkg)
+ self.assertIsInstance(inst["Epoch"], pywbem.Uint32,
+ "Epoch does not match for pkg %s" % pkg)
+ self.assertEqual(int(pkg.epoch), inst["Epoch"])
+ self.assertEqual(pkg.ver, inst["Version"],
+ "Version does not match for pkg %s" % pkg)
+ self.assertEqual(pkg.rel, inst["Release"],
+ "Release does not match for pkg %s" % pkg)
+ self.assertEqual(pkg.arch, inst["Architecture"],
+ "Architecture does not match for pkg %s" % pkg)
+
+ # try to leave out epoch part
+ if pkg.epoch == "0":
+ op_no_epoch = objpath.copy()
+ op_no_epoch["SoftwareElementID"] = util.make_nevra(pkg.name,
+ pkg.up_epoch, pkg.up_ver, pkg.up_rel, pkg.arch, "NEVER")
+ self.assertNotIn(":", op_no_epoch["SoftwareElementID"])
+ inst = self.conn.GetInstance(
+ InstanceName=op_no_epoch, LocalOnly=False)
+ self.assertIn(inst.path, (objpath, op_no_epoch))
+
+ @base.mark_tedious
+ def test_enum_instance_names_safe(self):
+ """
+ Tests EnumInstanceNames call on installed packages.
+ """
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertGreater(len(inames), 0)
+ for iname in inames:
+ self.assertIsInstance(iname, pywbem.CIMInstanceName)
+ self.assertEqual(iname.namespace, 'root/cimv2')
+ self.assertEqual(sorted(iname.keys()), sorted(self.KEYS))
+ nevra_set = set(i["InstanceID"] for i in inames)
+ for pkg in self.safe_pkgs:
+ self.assertIn('LMI:SoftwareIdentity:'+pkg.get_nevra(with_epoch="ALWAYS"),
+ nevra_set)
+
+# @base.mark_tedious
+# def test_enum_instances(self):
+# """
+# Tests EnumInstances call on installed packages.
+# """
+# insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+# self.assertGreater(len(insts), 0)
+# for inst in insts:
+# self.assertIsInstance(inst, pywbem.CIMInstance)
+# self.assertEqual(inst.namespace, 'root/cimv2')
+# self.assertEqual(sorted(inst.keys()), sorted(self.KEYS))
+# self.assertEqual(inst["InstanceID"], inst.path["InstanceID"])
+# nevra_set = set()
+# name_set = set()
+# for inst in insts:
+# nevra_set.add(inst["InstanceID"])
+# name_set.add(inst["Name"])
+# for pkg in self.safe_pkgs:
+# self.assertIn("LMI:SoftwareIdentity:"+pkg.get_nevra(with_epoch="ALWAYS"),
+# nevra_set)
+# self.assertIn(pkg.name, name_set)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestSoftwareIdentity)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_software_identity_resource.py b/src/software/test/test_software_identity_resource.py
new file mode 100755
index 0000000..34e5008
--- /dev/null
+++ b/src/software/test/test_software_identity_resource.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_SoftwareIdentityResource provider.
+"""
+
+from datetime import datetime
+import os
+import pywbem
+import socket
+import time
+import unittest
+
+import base
+import repository
+
+class TestSoftwareIdentityResource(
+ base.SoftwareBaseTestCase): #pylint: disable=R0904
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_SoftwareIdentityResource"
+ KEYS = ("CreationClassName", "Name", "SystemCreationClassName",
+ "SystemName")
+
+ @classmethod
+ def needs_pkgdb(cls):
+ return False
+
+ @classmethod
+ def needs_repodb(cls):
+ return True
+
+ def make_op(self, repo):
+ """
+ @param ses SoftwareElementState property value
+ @return object path of SoftwareIdentityResource
+ """
+ if isinstance(repo, repository.Repository):
+ repo = repo.repoid
+ objpath = self.objpath.copy()
+ objpath["Name"] = repo
+ objpath["SystemName"] = socket.gethostname()
+ return objpath
+
+ def tearDown(self):
+ for repo in self.repodb:
+ if repository.is_repo_enabled(repo) != repo.status:
+ repository.set_repo_enabled(repo, repo.status)
+
+ def _check_repo_instance(self, repo, inst):
+ """
+ Checks instance properties of repository.
+ """
+ objpath = self.make_op(repo)
+ self.assertEqual(objpath, inst.path)
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+ self.assertEqual(repo.repoid, inst["Name"])
+ self.assertIsInstance(inst["AccessContext"], pywbem.Uint16)
+ self.assertIsInstance(inst["AccessInfo"], basestring)
+ if repo.mirror_list:
+ self.assertEqual(inst["AccessInfo"], repo.mirror_list,
+ "AccessInfo does not match mirror_list for repo %s" %
+ repo.repoid)
+ elif repo.metalink:
+ self.assertEqual(inst["AccessInfo"], repo.metalink,
+ "AccessInfo does not match metalink for repo %s" %
+ repo.repoid)
+ else:
+ self.assertIn(inst["AccessInfo"], repo.base_urls,
+ "AccessInfo missing in base_urls for repo %s" % repo.repoid)
+ self.assertIsInstance(inst["AvailableRequestedStates"], list)
+ self.assertEqual(repo.name, inst["Caption"])
+ self.assertIsInstance(inst["Cost"], pywbem.Sint32)
+ self.assertIsInstance(inst["Description"], basestring)
+ self.assertEqual(repo.repoid, inst["ElementName"])
+ self.assertEqual(5, inst["EnabledDefault"])
+ self.assertEqual(2 if repo.status else 3, inst["EnabledState"],
+ "EnabledState does not match for repo %s" % repo.repoid)
+ self.assertEqual(3, inst["ExtendedResourceType"])
+ if repo.revision is None:
+ self.assertIsNone(inst["Generation"])
+ else:
+ self.assertEqual(repo.revision, inst["Generation"],
+ "Generation does not match for repo %s" % repo.repoid)
+ self.assertEqual(5, inst["HealthState"])
+ self.assertIsInstance(inst["GPGCheck"], bool)
+ self.assertEqual(200, inst["InfoFormat"])
+ self.assertEqual("LMI:SoftwareIdentityResource:"+repo.repoid,
+ inst["InstanceID"])
+ if repo.mirror_list is None and repo.metalink is None:
+ self.assertIsNone(inst["MirrorList"])
+ else:
+ self.assertEqual(
+ repo.metalink if repo.metalink else repo.mirror_list,
+ inst["MirrorList"])
+ self.assertEqual([2], inst["OperationalStatus"])
+ self.assertIsInstance(inst["OtherAccessContext"], basestring)
+ #self.assertEqual(repo.pkg_count, inst["PackageCount"],
+ #"PackageCount does not match for repo %s" % repo.repoid)
+ self.assertEqual(1, inst["PrimaryStatus"])
+ self.assertIsInstance(inst["RepoGPGCheck"], bool)
+ self.assertEqual(2 if repo.status else 3, inst["RequestedState"])
+ self.assertEqual(1, inst["ResourceType"])
+ self.assertIsInstance(inst["StatusDescriptions"], list)
+ self.assertEqual(1, len(inst["StatusDescriptions"]))
+ if repo.last_updated is None:
+ self.assertIsNone(inst["TimeOfLastUpdate"])
+ else:
+ time_stamp = repo.last_updated.replace(
+ microsecond=0, tzinfo=pywbem.cim_types.MinutesFromUTC(0))
+ self.assertEqual(time_stamp, inst["TimeOfLastUpdate"].datetime)
+ time_stamp = datetime.fromtimestamp(os.stat(repo.filename).st_mtime) \
+ .replace( microsecond=0
+ , tzinfo=pywbem.cim_types.MinutesFromUTC(0))
+ self.assertEqual(time_stamp, inst["TimeOfLastStateChange"].datetime)
+ self.assertEqual(12, inst["TransitioningToState"])
+
+ def test_get_instance_safe(self):
+ """
+ Tests GetInstance call.
+ """
+ for repo in self.repodb:
+ objpath = self.make_op(repo)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self._check_repo_instance(repo, inst)
+
+ @base.mark_dangerous
+ def test_get_instance(self):
+ """
+ Dangerous test, making sure, that properties are set correctly
+ for enabled and disabled repositories.
+ """
+ for repo in self.repodb:
+ objpath = self.make_op(repo)
+ self.assertIs(repository.is_repo_enabled(repo), repo.status)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(objpath, inst.path)
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+ self.assertEqual(repo.repoid, inst["Name"])
+ self.assertEqual(2 if repo.status else 3, inst["EnabledState"])
+ self.assertEqual(2 if repo.status else 3, inst["RequestedState"])
+ for repo in self.repodb:
+ objpath = self.make_op(repo)
+ time.sleep(1) # to make sure, that change will be noticed
+ repository.set_repo_enabled(repo, not repo.status)
+ self.assertIs(repository.is_repo_enabled(repo), not repo.status)
+ inst = self.conn.GetInstance(InstanceName=objpath)
+ self.assertEqual(repo.repoid, inst["Name"])
+ self.assertEqual(3 if repo.status else 2, inst["EnabledState"])
+ self.assertEqual(3 if repo.status else 2, inst["RequestedState"])
+
+ def test_enum_instance_names(self):
+ """
+ Tests EnumInstanceNames call on all repositories.
+ """
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertEqual(len(inames), len(self.repodb))
+ repoids = set(r.repoid for r in self.repodb)
+ for iname in inames:
+ self.assertIsInstance(iname, pywbem.CIMInstanceName)
+ self.assertEqual(iname.namespace, 'root/cimv2')
+ self.assertEqual(sorted(iname.keys()), sorted(self.KEYS))
+ self.assertIn(iname["Name"], repoids)
+ objpath = self.make_op(iname["Name"])
+ self.assertEqual(objpath, iname)
+
+ def test_enum_instances(self):
+ """
+ Tests EnumInstances call on all repositories.
+ """
+ insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ self.assertGreater(len(insts), 0)
+ repoids = dict((r.repoid, r) for r in self.repodb)
+ for inst in insts:
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ self.assertIn(inst["Name"], repoids)
+ self._check_repo_instance(repoids[inst["Name"]], inst)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestSoftwareIdentityResource)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/test_software_installed_package.py b/src/software/test/test_software_installed_package.py
deleted file mode 100755
index c66a941..0000000
--- a/src/software/test/test_software_installed_package.py
+++ /dev/null
@@ -1,351 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-"""
-Unit tests for LMI_SoftwareInstalledPackage provider.
-"""
-
-import os
-import pywbem
-import socket
-import stat
-import unittest
-
-import common
-
-def make_path_tuple(objpath):
- return tuple(objpath[a] for a in ("SoftwareElementID", "Name", "Version",
- "SoftwareElementState"))
-
-class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
- """
- Basic cim operations test.
- """
-
- CLASS_NAME = "LMI_SoftwareInstalledPackage"
- KEYS = ("Software", "System")
-
- def make_op(self, pkg, newer=True, ses=2):
- """
- @param ses SoftwareElementState property value
- @return object path of LMI_SoftwareInstaledPackage
- """
- objpath = self.objpath.copy()
- pkg_op = pywbem.CIMInstanceName(
- classname="LMI_SoftwarePackage", namespace="root/cimv2",
- keybindings={
- "Name" : pkg.name,
- "SoftwareElementID" : pkg.get_nevra(newer, 'ALWAYS'),
- "SoftwareElementState" : pywbem.Uint16(ses),
- "TargetOperatingSystem" : pywbem.Uint16(36),
- "Version" : getattr(pkg, 'up_ver' if newer else 'ver') })
- system_op = pywbem.CIMInstanceName(
- classname="Linux_ComputerSystem", namespace="root/cimv2",
- keybindings={
- "CreationClassName" : "Linux_ComputerSystem",
- "Name" : socket.gethostname() })
- objpath["Software"] = pkg_op
- objpath["System"] = system_op
- return objpath
-
- @common.mark_dangerous
- def test_get_instance(self):
- """
- Tests GetInstance call on packages from our rpm cache.
- TODO: test this in non-dangerous way
- """
- for pkg in self.pkgdb:
- if not common.is_installed(pkg):
- self.install_pkg(pkg)
- objpath = self.make_op(pkg)
- inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
- self.assertIsSubclass(inst.path.classname, self.CLASS_NAME)
- self.assertIsSubclass(
- inst.path['System'].classname, objpath['System'].classname)
- self.assertIsSubclass(inst.path['Software'].classname,
- objpath['Software'].classname)
- self.assertEqual(2, len(inst.path.keys()))
- for key in self.KEYS:
- self.assertEqual(inst[key], inst.path[key],
- "Object paths should be the same for %s"%pkg)
- self.assertEqual(inst['Software'], objpath['Software'],
- "Software key reference should match for %s"%pkg)
- common.remove_pkg(pkg.name)
- objpath['Software']['SoftwareElementState'] = pywbem.Uint16(1)
- with self.assertRaises(pywbem.CIMError) as cmngr:
- self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
- self.assertEqual(pywbem.CIM_ERR_NOT_FOUND, cmngr.exception.args[0],
- "Package %s should not be installed"%pkg)
-
- @common.mark_dangerous
- def test_enum_instance_names(self):
- """
- Tests EnumInstances call.
- TODO: test this in non-dangerous way
- """
- pkg = self.pkgdb[0] if len(self.pkgdb) > 0 else None
- if pkg and common.is_installed(pkg.name):
- common.remove_pkg(pkg.name)
- insts1 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
- if pkg:
- self.install_pkg(pkg)
- insts2 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
- self.assertEqual(len(insts1) + 1, len(insts2))
-
- if pkg:
- objpath = self.make_op(pkg)
- self.assertIn(make_path_tuple(objpath['Software']),
- set(make_path_tuple(inst['Software']) for inst in insts2))
- self.assertTrue(all(isinstance(inst, pywbem.CIMInstanceName)
- for inst in insts1))
-
- @common.mark_dangerous
- def test_enum_instances(self):
- """
- Tests EnumInstances call.
- """
- pkg = self.pkgdb[0] if len(self.pkgdb) > 0 else None
- if pkg and not common.is_installed(pkg.name):
- self.install_pkg(pkg.name)
- insts1 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
- if pkg:
- common.remove_pkg(pkg.name)
- insts2 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
- objpath = self.make_op(pkg)
- self.assertEqual(len(insts2) + 1, len(insts1))
- path_values = set(make_path_tuple(p["Software"]) for p in insts1)
- self.assertIn(make_path_tuple(objpath['Software']), path_values)
- path_values = set(make_path_tuple(p["Software"]) for p in insts2)
- self.assertNotIn(make_path_tuple(objpath['Software']), path_values)
-
- self.assertTrue(all(inst['Software'] == inst.path['Software']
- for inst in insts1))
- self.assertTrue(all(inst['System'] == inst.path['System']
- for inst in insts1))
-
- @common.mark_dangerous
- def test_create_instance(self):
- """
- Tests CreateInstance call.
- """
- for pkg in self.pkgdb:
- if common.is_installed(pkg.name):
- common.remove_pkg(pkg.name)
- self.assertFalse(common.is_installed(pkg.name),
- "Package %s should not be installed"%pkg)
- objpath = self.make_op(pkg, ses=1)
- inst = pywbem.CIMInstance(classname=objpath.classname, path=objpath)
- inst["Software"] = objpath["Software"]
- inst["System"] = objpath["System"]
- iname = self.conn.CreateInstance(NewInstance=inst)
- self.assertIsInstance(iname, pywbem.CIMInstanceName)
- objpath["Software"]["SoftwareElementState"] = pywbem.Uint16(2)
- self.assertEqual(iname["Software"], objpath["Software"],
- "Software key reference should match for %s"%pkg)
- self.assertIsInstance(iname["System"], pywbem.CIMInstanceName)
- self.assertIsSubclass(iname["System"].classname,
- objpath["System"].classname)
- self.assertEqual(set(iname.keys()), set(objpath.keys()),
- "Keys of object paths should be the same for %s"%pkg)
- self.assertTrue(common.is_installed(pkg.name),
- "Package %s should be installed"%pkg)
-
- with self.assertRaises(pywbem.CIMError) as cmngr:
- self.conn.CreateInstance(NewInstance=inst)
- self.assertEqual(pywbem.CIM_ERR_ALREADY_EXISTS,
- cmngr.exception.args[0],
- "Package %s should already be installed"%pkg)
-
- @common.mark_dangerous
- def test_delete_instance(self):
- """
- Tests DeleteInstance call.
- """
- for pkg in self.pkgdb:
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg)
- self.assertTrue(common.is_installed(pkg.name),
- "Package %s must be installed"%pkg)
- objpath = self.make_op(pkg)
- self.conn.DeleteInstance(objpath)
- self.assertFalse(common.is_installed(pkg.name),
- "Package %s must be uninstalled"%pkg)
- with self.assertRaises(pywbem.CIMError) as cmngr:
- self.conn.DeleteInstance(objpath)
- self.assertIn(cmngr.exception.args[0],
- [pywbem.CIM_ERR_FAILED, pywbem.CIM_ERR_NOT_FOUND],
- "Package %s can not be uninstalled again"%pkg)
-
- @common.mark_dangerous
- def test_check_integrity(self):
- """
- Tests CheckIntegrity call.
- TODO: test this in non-dangerous way
- """
- for pkg in self.pkgdb:
- files = self.pkg_files[pkg.name]
- if ( ( common.is_installed(pkg)
- and not common.verify_pkg(pkg.name))
- or ( common.is_installed(pkg.name)
- and not common.is_installed(pkg))) :
- common.remove_pkg(pkg.name)
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg)
- self.assertTrue(common.is_installed(pkg),
- "Package %s must be installed"%pkg)
-
- objpath = self.make_op(pkg)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="CheckIntegrity",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(0), rval,
- "IntegrityCheck should pass for %s"%pkg.name) # check passed
- self.assertEqual(1, len(oparms))
- self.assertIn("Failed", oparms)
- self.assertEqual(0, len(oparms["Failed"]),
- "IntegrityCheck should not fail for %s"%pkg.name)
-
- cnt_bad = 0
- for file_path in files:
- stats = os.lstat(file_path)
- if os.path.islink(file_path): # modify symbolic link
- target = os.readlink(file_path)
- os.remove(file_path)
- os.symlink(target, file_path) # just touch symlink
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="CheckIntegrity",
- ObjectName=objpath)
- # symlink must pass
- self.assertEqual(cnt_bad, len(oparms["Failed"]),
- "Symlink %s:%s should pass"%(pkg.name, file_path))
- os.remove(file_path)
- # now change target
- os.symlink("wrong_link_target", file_path)
- elif os.path.isdir(file_path): # check directory
- os.chmod(file_path, stats.st_mode) # just touch dir
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="CheckIntegrity",
- ObjectName=objpath)
- # dir must pass
- self.assertEqual(cnt_bad, len(oparms["Failed"]),
- "Directory %s:%s should pass"%(pkg.name, file_path))
- # modify read access of directory
- os.chmod(file_path, stats.st_mode ^ stat.S_IROTH)
- else: # modify regular file
- # just touch file - this is enough to make it fail
- with open(file_path, "w+"):
- pass
- cnt_bad += 1
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="CheckIntegrity",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(1), rval,
- "File %s:%s should not pass"%(pkg.name, file_path))
- self.assertEqual(1, len(oparms))
- self.assertIn("Failed", oparms)
- self.assertEqual(len(oparms["Failed"]), cnt_bad,
- "Number of errors not correct. Failed for %s:%s" % (
- pkg.name, file_path))
- self.assertIn(file_path, (p["Name"] for p in oparms["Failed"]),
- "File %s:%s should also be in failed"%(pkg.name, file_path))
-
- @common.mark_dangerous
- def test_method_update(self):
- """
- Tests Update method invocation.
- """
- for pkg in self.pkgdb:
- if ( common.is_installed(pkg.name)
- and not common.is_installed(pkg, False)):
- common.remove_pkg(pkg.name)
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg, False)
- self.assertTrue(common.is_installed(pkg, False),
- "Package %s must be installed"%pkg.get_nevra(False))
-
- objpath = self.make_op(pkg, False)
- op_up = self.make_op(pkg)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Update",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint16(1), rval,
- "Update should succed for %s"%pkg.get_nevra(False))
- self.assertEqual(1, len(oparms))
- self.assertIn("Installed", oparms)
- self.assertEqual(oparms["Installed"], op_up["Software"],
- "Object paths should match for %s"%pkg)
- self.assertTrue(common.is_installed(pkg),
- "Package %s must be installed"%pkg)
-
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Update",
- ObjectName=op_up)
- self.assertEqual(pywbem.Uint16(0), rval,
- "Package %s should be already updated"%pkg)
- self.assertEqual(1, len(oparms))
- self.assertEqual(oparms["Installed"], op_up["Software"],
- "Object paths should match for %s"%pkg)
- self.assertTrue(common.is_installed(pkg.name),
- "Package %s must be installed"%pkg)
- self.assertFalse(common.is_installed(pkg, False),
- "Older package %s must not be installed" %
- pkg.get_nevra(False))
-
- with self.assertRaises(pywbem.CIMError) as cmngr:
- self.conn.InvokeMethod(
- MethodName="Update",
- ObjectName=objpath)
- self.assertEqual(pywbem.CIM_ERR_NOT_FOUND, cmngr.exception.args[0],
- "Older package %s should not be installed" %
- pkg.get_nevra(False))
-
- common.remove_pkg(pkg.name)
- self.install_pkg(pkg, False)
- self.assertTrue(common.is_installed(pkg, False))
-
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Update",
- ObjectName=objpath,
- Epoch=pkg.epoch,
- Version=pkg.ver,
- Release=pkg.rel)
- self.assertEqual(pywbem.Uint16(0), rval,
- "Update of package %s should succeed"%pkg)
- self.assertEqual(oparms["Installed"], objpath["Software"],
- "Object paths should be the same for package %s"%pkg)
-
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Update",
- ObjectName=objpath,
- Epoch=pkg.up_epoch,
- Version=pkg.up_ver,
- Release=pkg.up_rel)
- self.assertEqual(pywbem.Uint16(1), rval,
- "Package %s can not be updated twice to highest version"%pkg)
- self.assertEqual(oparms["Installed"], op_up["Software"],
- "Object paths should match for package %s"%pkg)
-
-def suite():
- """For unittest loaders."""
- return unittest.TestLoader().loadTestsFromTestCase(
- TestSoftwareInstalledPackage)
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/software/test/test_software_package.py b/src/software/test/test_software_package.py
deleted file mode 100755
index b619da7..0000000
--- a/src/software/test/test_software_package.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-"""
-Unit tests for LMI_SoftwareInstalledPackage provider.
-"""
-
-import pywbem
-import unittest
-
-import common
-import rpmcache
-
-class TestSoftwarePackage(common.SoftwareBaseTestCase): #pylint: disable=R0904
- """
- Basic cim operations test.
- """
-
- CLASS_NAME = "LMI_SoftwarePackage"
- KEYS = ( "Name", "SoftwareElementID", "SoftwareElementState"
- , "TargetOperatingSystem", "Version")
-
- def make_op(self, pkg, newer=True, ses=2):
- """
- @param ses SoftwareElementState property value
- @return object path of SoftwarePackage
- """
- objpath = self.objpath.copy()
- objpath["Name"] = pkg.name
- objpath["SoftwareElementID"] = pkg.get_nevra(newer, "ALWAYS")
- objpath["SoftwareElementState"] = pywbem.Uint16(ses)
- objpath["TargetOperatingSystem"] = pywbem.Uint16(36)
- objpath["Version"] = getattr(pkg, 'up_ver' if newer else 'ver')
- return objpath
-
- @common.mark_dangerous
- def test_get_instance(self):
- """
- Tests GetInstance call on packages from our rpm cache.
- TODO: test this in non-dangerous way
- """
- for pkg in self.pkgdb:
- if common.is_installed(pkg.name):
- common.remove_pkg(pkg.name)
- objpath = self.make_op(pkg, ses=1)
- inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
- self.assertEqual(inst.path, objpath,
- "Object paths should match for %s"%pkg)
- for key in self.KEYS:
- self.assertTrue(inst.properties.has_key(key),
- "OP is missing \"%s\" key for package %s"%(key, pkg))
- if key == "TargetOperatingSystem":
- self.assertIsInstance(inst.path[key], (int, long))
- else:
- self.assertEqual(inst.path[key], inst[key],
- "Object paths of instance should match for %s"%pkg)
- self.assertEqual(pkg.up_rel, inst['Release'],
- "Release property should match for %s"%pkg)
- self.install_pkg(pkg)
- objpath['SoftwareElementState'] = pywbem.Uint16(2)
- inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
- self.assertEqual(inst.path, objpath,
- "Object paths should match for %s"%pkg)
-
- # try to leave out epoch part
- if pkg.epoch == "0":
- op_no_epoch = objpath.copy()
- op_no_epoch["SoftwareElementID"] = rpmcache.make_nevra(pkg.name,
- pkg.up_epoch, pkg.up_ver, pkg.up_rel, pkg.arch, "NEVER")
- self.assertNotIn(":", op_no_epoch["SoftwareElementID"])
- inst = self.conn.GetInstance(
- InstanceName=op_no_epoch, LocalOnly=False)
- self.assertIn(inst.path, (objpath, op_no_epoch))
-
- @common.mark_dangerous
- def test_method_install(self):
- """
- Tests Install method invocation.
- """
- for pkg in self.pkgdb:
- if common.is_installed(pkg.name):
- common.remove_pkg(pkg.name)
- objpath = self.make_op(pkg, ses=1)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Install",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(1), rval,
- "Installation of %s should be successful"%pkg)
- self.assertEqual(1, len(oparms))
- self.assertTrue(oparms.has_key('Installed'))
- objpath['SoftwareElementState'] = pywbem.Uint16(2)
- self.assertEqual(oparms['Installed'], objpath,
- "Object paths should match for %s"%pkg)
- self.assertTrue(common.is_installed(pkg.name),
- "Package %s must be installed"%pkg)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Install",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(0), rval,
- "Installation of %s should fail"%pkg)
- self.assertEqual(len(oparms), 1)
- self.assertEqual(oparms['Installed'], objpath,
- "Object paths should match for %s"%pkg)
-
- @common.mark_dangerous
- def test_method_remove(self):
- """
- Tests Remove method invocation.
- """
- for pkg in self.pkgdb:
- if not common.is_installed(pkg.name):
- self.install_pkg(pkg)
- objpath = self.make_op(pkg)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Remove",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint16(1), rval,
- "Removal of package should succeed"%pkg)
- self.assertEqual(0, len(oparms))
- self.assertFalse(common.is_installed(pkg.name),
- "Package %s should not be installed"%pkg)
- objpath["SoftwareElementState"] = pywbem.Uint16(1)
- (rval, oparms) = self.conn.InvokeMethod(
- MethodName="Remove",
- ObjectName=objpath)
- self.assertEqual(pywbem.Uint32(0), rval,
- "Removal of %s should fail"%pkg)
- self.assertEqual(len(oparms), 0)
-
-def suite():
- """For unittest loaders."""
- return unittest.TestLoader().loadTestsFromTestCase(
- TestSoftwarePackage)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/src/software/test/test_system_software_collection.py b/src/software/test/test_system_software_collection.py
new file mode 100755
index 0000000..d68da47
--- /dev/null
+++ b/src/software/test/test_system_software_collection.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Unit tests for LMI_SoftwareIdentity provider.
+"""
+
+import unittest
+
+import base
+
+class TestSystemSoftwareCollection(
+ base.SoftwareBaseTestCase): #pylint: disable=R0904
+ """
+ Basic cim operations test.
+ """
+
+ CLASS_NAME = "LMI_SystemSoftwareCollection"
+ KEYS = ("InstanceID", )
+
+ def make_op(self):
+ """
+ @param ses SoftwareElementState property value
+ @return object path of SoftwareIdentity
+ """
+ objpath = self.objpath.copy()
+ objpath["InstanceID"] = "LMI:SystemSoftwareCollection"
+ return objpath
+
+ def test_get_instance(self):
+ """
+ Tests GetInstance call on packages from our rpm cache.
+ """
+ objpath = self.make_op()
+ inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
+
+ self.assertEqual(objpath, inst.path)
+ self.assertEqual(list(objpath.keys()), list(inst.path.keys()))
+ for key in self.KEYS:
+ self.assertEqual(inst[key], objpath[key])
+ self.assertIsInstance(inst["Caption"], basestring)
+
+ def test_enum_instance_names(self):
+ """
+ Tests EnumInstanceNames call on installed packages.
+ """
+ inames = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertEqual(len(inames), 1)
+ iname = inames[0]
+ self.assertEqual(iname, self.make_op())
+
+ def test_enum_instances(self):
+ """
+ Tests EnumInstances call on installed packages.
+ """
+ insts = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ self.assertEqual(len(insts), 1)
+ inst = insts[0]
+ self.assertEqual(inst.path, self.make_op())
+ self.assertEqual(inst["InstanceID"], inst.path["InstanceID"])
+ self.assertIsInstance(inst["Caption"], basestring)
+
+def suite():
+ """For unittest loaders."""
+ return unittest.TestLoader().loadTestsFromTestCase(
+ TestSystemSoftwareCollection)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/software/test/util.py b/src/software/test/util.py
new file mode 100644
index 0000000..958f1d3
--- /dev/null
+++ b/src/software/test/util.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+# -*- Coding:utf-8 -*-
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details. #
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Radek Novacek <rnovacek@redhat.com>
+# Authors: Michal Minar <miminar@redhat.com>
+
+"""
+Common test utilities.
+"""
+
+import re
+from subprocess import check_output
+
+RE_NEVRA = re.compile(
+ r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+RE_NEVRA_OPT_EPOCH = re.compile(
+ r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+RE_ENVRA = re.compile(
+ r'^(?P<epoch>\d+|\(none\)):(?P<name>.+)-(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+)$')
+
+def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
+ """
+ @param with_epoch may be one of:
+ "NOT_ZERO" - include epoch only if it's not zero
+ "ALWAYS" - include epoch always
+ "NEVER" - do not include epoch at all
+ """
+ estr = ''
+ if with_epoch.lower() == "always":
+ estr = epoch
+ elif with_epoch.lower() == "not_zero":
+ if epoch and epoch.lower() not in {"0", "(none)"}:
+ estr = epoch
+ if len(estr):
+ estr += ":"
+ return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
+
+def make_evra(epoch, ver, rel, arch):
+ """ @return evra string """
+ if not epoch or epoch.lower() == "(none)":
+ epoch = "0"
+ return "%s:%s-%s.%s" % (epoch, ver, rel, arch)
+
+def run_yum(*params, **kwargs):
+ """
+ Runs yum with params and returns its output
+ It's here especially to allow pass a repolist argument, that
+ specifies list of repositories, to run the command on.
+ """
+ cmd = ['yum'] + list(params)
+ repolist = kwargs.get('repolist', None)
+ if repolist is None:
+ repolist = []
+ if repolist:
+ cmd += ['--disablerepo=*']
+ cmd += ['--enablerepo='+r for r in repolist]
+ return check_output(cmd)
+
+def get_system_architecture():
+ """
+ @return the system architecture name as seen by rpm
+ """
+ return check_output(['rpm', '-q', '--qf', '%{ARCH}\n', 'rpm'])
+
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..04d11b7
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+install(PROGRAMS openlmi-doc-class2rst openlmi-doc-class2uml DESTINATION bin)
+
diff --git a/tools/class2uml.py b/tools/class2uml.py
deleted file mode 100755
index e5e6ea4..0000000
--- a/tools/class2uml.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Jan Safranek <jsafrane@redhat.com>
-#
-
-import os;
-import pywbem;
-import optparse
-import re
-import cgi
-import sys
-
-class UmlExporter(object):
-
- def __init__(self, cliconn):
- self.classcache = {}
- self.cliconn = cliconn
- # original list of classes
- self.classes = set()
- self.file = sys.stdout
-
- def load_class(self, classname):
- if classname in self.classcache:
- return self.classcache[classname]
-
- c = self.cliconn.GetClass(classname)
- self.classcache[classname] = c
- return c
-
- def display_type(self, param):
- """
- Return displayable type of given parameter.
- It adds [] if it's array and class name of referenced classes.
- """
- if param.reference_class:
- ptype = param.reference_class
- else:
- ptype = param.type
- if param.is_array:
- ptype = ptype + "[]"
- return ptype
-
- def print_parameters(self, params):
- """
- Print table of method parametes.
- """
- if not params:
- print >>self.file, "None"
- return
-
- print >>self.file, "<table class=\"parameters\">"
- for p in params.values():
- direction = set()
- if p.qualifiers.has_key("Out"):
- direction.add("OUT")
- if p.qualifiers.has_key("In"):
- direction.add("IN")
- if not direction:
- direction.add("IN")
- direction = "/".join(sorted(direction))
-
- print >>self.file, "<tr>"
- print >>self.file, "<td>%s</td><td>%s</td><td><b>%s</b></td>" % (direction, self.display_type(p), p.name)
- print >>self.file, "<td><table class=\"qualifiers\">"
- self.print_qualifiers(p.qualifiers.values())
- print >>self.file, "</table></td>"
- print >>self.file, "</tr>"
- print >>self.file, "</table>"
-
- def print_qualifiers(self, qualifiers):
- """
- Print contenf of table of qualifiers.
- Only Deprecated, Description, Values and ValueMap are recognized.
- """
- deprecated = ""
- for q in sorted(qualifiers):
- if q.name == "Deprecated":
- deprecated = "<div class=\"deprecated\">DEPRECATED</div> "
- if q.name == "Description":
- print >>self.file, "<tr><td class=\"qualifiers\" colspan=\"2\">%s%s</td></tr>" % (deprecated, self.escape(q.value))
- if q.name == "Values":
- print >>self.file, "<tr><td class=\"qualifiers\"><b>Values</b></td> <td class=\"qualifiers\"><table><tr><td class=\"qualifiers\">%s</td></tr></table></td></tr>" % ("</td></tr><tr><td class=\"qualifiers\">".join(q.value))
- if q.name == "ValueMap":
- print >>self.file, "<tr><td class=\"qualifiers\"><b>ValueMap</b></td> <td class=\"qualifiers\"><table><tr><td class=\"qualifiers\">%s</td></tr></table></td></tr>" % ("</td></tr><tr><td class=\"qualifiers\">".join(q.value))
-
- def compare_properties(self, p1, p2):
- """
- Compare two properties if they should printed in Inherited properties.
- Only Name, Description and Implemented are checked.
- Returns False, if the property should be printed in Local.
- """
- if p1.name != p2.name:
- return False
- d1 = p1.qualifiers.get("Description", None)
- d2 = p2.qualifiers.get("Description", None)
- if d1.value != d2.value:
- return False
- i1 = p1.qualifiers.get("Implemented", None)
- i2 = p2.qualifiers.get("Implemented", None)
- if i1 and i1.value and not (i2 and i2.value):
- return False
- return True
-
- def print_class(self, c, display_local = True, box_only = False):
- """
- Print one class, inc. header.
- """
- parent = None
- if c.superclass:
- parent = self.load_class(c.superclass)
-
- if c.superclass:
- # draw arrow to parent
- print >>self.file, "%s -down-|> %s" % (c.classname, c.superclass)
-
- if box_only:
- self.file.write("class %s\n\n" % c.classname)
- return
-
- local_props = []
- for name in sorted(c.properties.keys()):
- if parent and parent.properties.has_key(name):
- if not self.compare_properties(c.properties[name], parent.properties[name]):
- # the property was overridden
- local_props.append(c.properties[name])
- else:
- local_props.append(c.properties[name])
-
- local_methods = []
- for name in sorted(c.methods.keys()):
- if parent and parent.methods.has_key(name):
- if not self.compare_properties(c.methods[name], parent.methods[name]):
- # the property was overridden
- local_methods.append(c.methods[name])
- else:
- local_methods.append(c.methods[name])
- self.file.write("class %s {\n" % c.classname)
-
- if display_local:
- for prop in local_props:
- self.file.write(" %s %s\n" % (self.display_type(prop), prop.name))
-
-
- if local_methods:
- for m in local_methods:
- self.file.write(" %s()\n" % (m.name))
-
- self.file.write("}\n")
- self.file.write("url of %s is [[%s.html]]\n" % (c.classname, c.classname))
-
- def add_class(self, classname):
- self.classes.add(classname)
-
- def export(self, shrink, noassoc = False):
- """
- Print all classes and their parents.
- """
- print >>self.file, "@startuml"
- while self.classes:
- c = self.classes.pop()
-
- cl = self.load_class(c)
- if noassoc and cl.qualifiers.get("Association", False):
- continue
-
- if shrink and shrink.match(c):
-
- self.print_class(cl, box_only = True)
- else:
- self.print_class(cl)
-
- print >>self.file, "@enduml"
-
-description = """
-Generate UML image for given classes. The tool connects to specified CIMOM
-and reads class definition from there. Each class specified on command line
-will be drawn as one box, containing locally defined or re-defined properties
-and methods. Inheritance will be shown as arrow between a parent class and a
-subclass.
-
-The generated file can be coverted to a picture by PlantUML tool.
-"""
-
-parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description)
-parser.add_option('-u', '--url', action='store', dest='addr', default='https://localhost:5989', help='URL of CIM server, default: https://localhost:5989')
-parser.add_option('-U', '--user', action='store', dest='user', default=None, help='CIM user name')
-parser.add_option('-s', '--shrink', action='store', dest='shrink', default=None, help='Regular expression pattern of CIM classes, which will be drawn only as boxes, without properties.')
-parser.add_option('-A', '--no-associations', action='store_true', dest='noassoc', default=False, help='Skip association classes.')
-parser.add_option('-P', '--password', action='store', dest='password', default=None, help='CIM password')
-(options, args) = parser.parse_args()
-
-sys.stdout.softspace=0
-
-shrink = None
-if options.shrink:
- shrink = re.compile(options.shrink)
-
-cliconn = pywbem.WBEMConnection(options.addr, (options.user, options.password))
-exporter = UmlExporter(cliconn)
-for c in args:
- exporter.add_class(c)
-exporter.export(shrink = shrink, noassoc = options.noassoc)
-
diff --git a/tools/class2rst.py b/tools/openlmi-doc-class2rst
index 12342f6..34d847d 100755..100644
--- a/tools/class2rst.py
+++ b/tools/openlmi-doc-class2rst
@@ -1,6 +1,6 @@
#!/usr/bin/python
#
-# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -17,24 +17,24 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors: Jan Safranek <jsafrane@redhat.com>
+# Radek Novacek <rnovacek@redhat.com>
#
-import os;
-import pywbem;
+import os
+import konkretmof
import optparse
import re
import cgi
import sys
class HtmlExporter(object):
-
- def __init__(self, cliconn):
+
+ def __init__(self):
self.classcache = {}
- self.cliconn = cliconn
# original list of classes
self.classes = []
- # set of all classes to print, i.e. incl. all parents
- self.queue = set()
+ # list of all classes to print, i.e. incl. all parents
+ self.queue = []
self.file = None
def link(self, classname, attributename = ""):
@@ -42,23 +42,15 @@ class HtmlExporter(object):
link = classname + "-" + attributename
else:
link = classname
- link = link.replace("_","-")
+ link = str(link).replace("_","-")
return link
-
+
def print_escaped(self, string, prefix = ""):
# replace \n with prefixes
- lines = string.split("\\n")
+ lines = string.split("\n")
for line in lines:
print >>self.file, prefix + line
print >>self.file, ""
-
- def load_class(self, classname):
- if classname in self.classcache:
- return self.classcache[classname]
-
- c = self.cliconn.GetClass(classname)
- self.classcache[classname] = c
- return c
def source_class(self, c, param):
"""
@@ -67,21 +59,20 @@ class HtmlExporter(object):
"""
while True:
-
- if isinstance(param, pywbem.CIMMethod):
- if not c.methods.has_key(param.name):
+ if isinstance(param, konkretmof.MOF_Method_Decl):
+ if not c.methods().has_key(param.name):
break
else:
- if not c.properties.has_key(param.name):
+ if not c.properties().has_key(param.name):
break
- if not c.superclass:
+ if not c.super_class:
# we're at the top class
- return c.classname
-
+ return c.name
+
parent = c
- c = self.load_class(c.superclass)
- return parent.classname
+ c = c.super_class
+ return parent.name
def display_type(self, param):
"""
@@ -89,12 +80,17 @@ class HtmlExporter(object):
It adds [] if it's array and class name of referenced classes.
"""
url = False
- if param.reference_class:
- ptype = param.reference_class
+ if isinstance(param, konkretmof.MOF_Reference_Decl):
+ ptype = param.class_name
+ url = True
+ elif isinstance(param, konkretmof.MOF_Parameter) and (param.data_type == konkretmof.TOK_REF):
+ ptype = param.ref_name
+ self.add_class(param.ref_name)
url = True
else:
- ptype = param.type
- if param.is_array:
+ ptype = param.type_name()
+
+ if isinstance(param, (konkretmof.MOF_Property_Decl, konkretmof.MOF_Parameter)) and param.array_index != 0:
ptype = ptype + "[]"
if url:
ptype = ":ref:`%s <%s>`" % (ptype, self.link(ptype))
@@ -111,33 +107,34 @@ class HtmlExporter(object):
print >>self.file, "*None*"
return
- for p in params.values():
+ for p in params:
param_indent = " "*8
direction = set()
- if p.qualifiers.has_key("Out"):
+ if p.qualifiers.has_key("out"):
direction.add("*OUT*")
- if p.qualifiers.has_key("In"):
- if p.qualifiers["In"].value:
+ if p.qualifiers.has_key("in"):
+ if p.qualifiers.get("in").params.value():
direction.add("*IN*")
if not direction:
direction.add("*IN*")
direction = ", ".join(sorted(direction))
print >>self.file, param_indent + "%s %s **%s**" % (direction, self.display_type(p), p.name)
- self.print_qualifiers(p.qualifiers.values(), param_indent+ " "*4)
+ self.print_qualifiers(p.qualifiers, param_indent+ " "*4)
print >>self.file, param_indent
print >>self.file, function_indent
def print_prototype(self, method):
""" Print function prototype """
- self.file.write("``" + method.return_type + "`` ")
+ self.file.write("``" + method.type_name() + "`` ")
self.file.write("**" + method.name + "** (")
params = []
- for p in method.parameters.values():
- params.append((self.display_type(p) + " " + p.name))
+ if method.parameters:
+ for p in method.parameters:
+ params.append((self.display_type(p) + " " + p.name))
self.file.write(", ".join(params))
- self.file.write("\n")
-
+ self.file.write(")\n")
+
def print_table(self, col1, col2, indent):
"""
Print table with two columns.
@@ -161,151 +158,160 @@ class HtmlExporter(object):
values = None
maps = None
for q in sorted(qualifiers):
- if q.name == "Deprecated":
+ if not q:
+ continue
+ qname = q.name.lower()
+ if qname == "deprecated":
deprecated = "**Deprecated!** "
- if q.name == "Description":
+ if qname == "description":
if deprecated:
print >>self.file, indent + deprecated
- self.print_escaped(q.value, indent)
- if q.name == "Values":
- values = q.value
- if q.name == "ValueMap":
- maps = q.value
+ self.print_escaped(q.params.value(), indent)
+ if qname == "values":
+ values = []
+ for p in q.params:
+ values.append(p.value())
+ if qname == "valuemap":
+ maps = []
+ for p in q.params:
+ maps.append(p.value())
print >>self.file, indent
if maps and values:
self.print_table(maps, values, indent)
print >>self.file, indent
- def compare_properties(self, p1, p2):
- """
- Compare two properties if they should printed in Inherited properties.
- Only Name, Description and Implemented are checked.
- Returns False, if the property should be printed in Local.
- """
- if p1.name != p2.name:
- return False
- d1 = p1.qualifiers.get("Description", None)
- d2 = p2.qualifiers.get("Description", None)
- if d1.value != d2.value:
- return False
- i1 = p1.qualifiers.get("Implemented", None)
- i2 = p2.qualifiers.get("Implemented", None)
- if i1 and i1.value and not (i2 and i2.value):
- return False
- return True
-
- def print_keys(self, c):
+ def print_keys(self, class_hiearchy):
print >>self.file, "Key properties"
print >>self.file, "^^^^^^^^^^^^^^"
print >>self.file, ""
- for prop in c.properties.values():
- if prop.qualifiers.has_key('Key'):
- src = self.source_class(c, prop)
- link = self.link(src, prop.name)
- print >>self.file, "| :ref:`%s <%s>`" % (prop.name, link)
+ for cls in class_hiearchy:
+ for prop in cls.properties().values():
+ if prop.qualifiers.has_key('key'):
+ src = self.source_class(cls, prop)
+ link = self.link(src, prop.name)
+ print >>self.file, "| :ref:`%s <%s>`" % (prop.name, link)
print >>self.file, ""
def print_class(self, c):
"""
Print one class, inc. header.
"""
+ # We want to print following qualifiers for properties/methods
+ known_qualifiers = ("deprecated", "description", "values", "valuemap")
parent = None
- if c.superclass:
- parent = self.load_class(c.superclass)
- print >>self.file, "Subclass of :ref:`%s <%s>`" % (c.superclass, self.link(c.superclass))
+ class_hiearchy = [c]
+ cc = c
+ while cc.super_class:
+ cc = cc.super_class
+ class_hiearchy.append(cc)
+
+ if c.super_class:
+ parent = c.super_class
+ print >>self.file, "Subclass of :ref:`%s <%s>`" % (c.super_class_name, self.link(c.super_class_name))
print >>self.file, ""
- description = c.qualifiers.get("Description", None)
- if not description:
- description = parent.qualifiers.get("Description", None)
- if description:
- self.print_escaped(description.value, "")
+ for cls in class_hiearchy:
+ description = cls.qualifiers.get("description")
+ if description:
+ self.print_escaped(description.params.value(), "")
+ break
print >>self.file, ""
- self.print_keys(c)
-
- local_props = []
- inherited_props = []
- for name in sorted(c.properties.keys()):
- if parent and parent.properties.has_key(name):
- inherited_props.append(c.properties[name])
- if not self.compare_properties(c.properties[name], parent.properties[name]):
- # the property was overridden
- local_props.append(c.properties[name])
- else:
- local_props.append(c.properties[name])
-
- local_methods = []
- inherited_methods = []
- for name in sorted(c.methods.keys()):
- if parent and parent.methods.has_key(name):
- inherited_methods.append(c.methods[name])
- if not self.compare_properties(c.methods[name], parent.methods[name]):
- # the property was overridden
- local_methods.append(c.methods[name])
- else:
- local_methods.append(c.methods[name])
+ self.print_keys(class_hiearchy)
+
+ # Create dictionaries with properties and methods of given class and fill them
+ properties = {}
+ methods = {}
+ for cls in class_hiearchy:
+ for prop in cls.properties().values():
+ if prop.name not in properties.keys():
+ properties[prop.name] = prop
+ prop.cls = cls
+ prop.is_local = cls == c
+ prop.known_qualifiers = {}
+ for q in known_qualifiers:
+ prop.known_qualifiers[q] = prop.qualifiers.get(q)
+ else:
+ inherited_prop = properties[prop.name]
+ for q in known_qualifiers:
+ if not inherited_prop.known_qualifiers[q]:
+ inherited_prop.known_qualifiers[q] = prop.qualifiers.get(q)
+ for meth in cls.methods().values():
+ if meth.name not in methods.keys():
+ methods[meth.name] = meth
+ meth.cls = cls
+ meth.is_local = cls.name == c.name
+ meth.known_qualifiers = {}
+ for q in known_qualifiers:
+ meth.known_qualifiers[q] = meth.qualifiers.get(q)
+ else:
+ inherited_meth = methods[meth.name]
+ for q in known_qualifiers:
+ if not inherited_meth.known_qualifiers[q]:
+ inherited_meth.known_qualifiers[q] = meth.qualifiers.get(q)
print >>self.file, "Local properties"
print >>self.file, "^^^^^^^^^^^^^^^^"
print >>self.file, ""
- if local_props:
- for prop in local_props:
- prop_link = self.link(c.classname, prop.name)
+ present = False
+ for prop in properties.values():
+ if prop.is_local:
+ present = True
+ prop_link = self.link(c.name, prop.name)
print >>self.file, ".. _%s:" % (prop_link)
print >>self.file, ""
print >>self.file, "%s **%s**" % (self.display_type(prop), prop.name)
print >>self.file, ""
- self.print_qualifiers(prop.qualifiers.values(), " "*4)
- print >>self.file, ""
- else:
+ self.print_qualifiers(prop.known_qualifiers.values(), " "*4)
+ if not present:
print >>self.file, "*None*"
- print >>self.file, ""
+ print >>self.file, ""
print >>self.file, "Local methods"
print >>self.file, "^^^^^^^^^^^^^"
print >>self.file, ""
- if local_methods:
- for m in local_methods:
- link = self.link(c.classname, m.name)
+ present = False
+ for m in methods.values():
+ if m.is_local:
+ present = True
+ link = self.link(c.name, m.name)
print >>self.file, " .. _%s:" % (link)
print >>self.file, ""
self.print_prototype(m)
print >>self.file, ""
- self.print_qualifiers(m.qualifiers.values(), " "*4)
+ self.print_qualifiers(m.known_qualifiers.values(), " "*4)
print >>self.file, " **Parameters**"
print >>self.file, " "
self.print_parameters(m.parameters)
- print >>self.file, " "
- else:
+ if not present:
print >>self.file, "*None*"
- print >>self.file, ""
+ print >>self.file, ""
print >>self.file, "Inherited properties"
print >>self.file, "^^^^^^^^^^^^^^^^^^^^"
print >>self.file, ""
- if inherited_props:
- for p in inherited_props:
- src = self.source_class(c, p)
- link = self.link(src, p.name)
- print >>self.file, "| %s :ref:`%s <%s>`" % (self.display_type(p), p.name, link)
- print >>self.file, ""
- else:
+ present = False
+ for prop in properties.values():
+ if not prop.is_local:
+ present = True
+ link = self.link(prop.cls.name, prop.name)
+ print >>self.file, "| %s :ref:`%s <%s>`" % (self.display_type(prop), prop.name, link)
+ if not present:
print >>self.file, "*None*"
- print >>self.file, ""
+ print >>self.file, ""
print >>self.file, "Inherited methods"
print >>self.file, "^^^^^^^^^^^^^^^^^"
print >>self.file, ""
- if inherited_methods:
- for m in inherited_methods:
- src = self.source_class(c, m)
- link = self.link(src, m.name)
+ present = False
+ for m in methods.values():
+ if not m.is_local:
+ present = True
+ link = self.link(m.cls.name, m.name)
print >>self.file, "| :ref:`%s <%s>`" % (m.name, link)
- print >>self.file, ""
- else:
+ if not present:
print >>self.file, "*None*"
- print >>self.file, ""
+ print >>self.file, ""
def print_file(self, filename):
@@ -315,46 +321,44 @@ class HtmlExporter(object):
f.close()
def print_page(self, c, header, footer):
- print "exporting ", c.classname
- self.file = open(c.classname + ".rst", "w")
- print >>self.file, ".. _%s:" % self.link(c.classname)
+ print "exporting ", c.name
+ self.file = open(c.name + ".rst", "w")
+ print >>self.file, ".. _%s:" % self.link(c.name)
print >>self.file, ""
- print >>self.file, c.classname
- print >>self.file, "-"*len(c.classname)
+ print >>self.file, c.name
+ print >>self.file, "-"*len(c.name)
print >>self.file, ""
-# if header:
-# self.print_file(header)
+ if header:
+ self.print_file(header)
self.print_class(c)
-# if footer:
-# self.print_file(footer)
+ if footer:
+ self.print_file(footer)
self.file.close()
-
+
def add_class(self, classname):
+ if classname in self.classes:
+ return
+ c = konkretmof.MOF_Class_Decl.list.fget().find(classname)
+ if not c:
+ print 'No such class "%s"' % classname
+ return
self.classes.append(classname)
- print "adding", classname
- self.queue.add(classname)
- # add all parents
- c = self.load_class(classname)
- parentname = c.superclass
- while parentname:
- if parentname in self.queue:
- break
- print "adding", parentname
- self.queue.add(parentname)
- c = self.load_class(parentname)
- parentname = c.superclass
+ print "adding", c.name
+ self.queue.append(c)
+ if c.super_class:
+ self.add_class(c.super_class_name)
def export(self, header=None, footer=None):
"""
Print everything, i.e. index.rst + all classes + all their parents.
"""
# remove duplicate classes from the queue (and sort them)
- self.queue = sorted(self.queue)
+ self.queue.sort(key=lambda c:c.name)
self.print_tree_rst(header, footer)
self.print_index_rst(header, footer)
while self.queue:
c = self.queue.pop()
- self.print_page(self.load_class(c), header, footer)
+ self.print_page(c, header, footer)
def _do_print_tree(self, name, subtree, level):
nbsp=" |nbsp| "
@@ -378,24 +382,23 @@ class HtmlExporter(object):
# hash classname -> nr. of its parents
parents = {}
# initialize the hash
- for cname in self.queue:
- subclasses[cname] = {}
- parents[cname] = 0
+ for c in self.queue:
+ subclasses[c.name] = {}
+ parents[c.name] = 0
# fill the hash
- for cname in self.queue:
- c = self.load_class(cname)
- if c.superclass:
- subclasses[c.superclass][cname] = subclasses[cname]
- parents[cname] += 1
+ for c in self.queue:
+ if c.super_class:
+ subclasses[c.super_class_name][c.name] = subclasses[c.name]
+ parents[c.name] += 1
print >>self.file, ".. |nbsp| unicode:: 0xA0"
print >>self.file, " :trim:"
print >>self.file, ""
# print all top classes
- for cname in self.queue:
- if parents[cname] == 0:
- self._do_print_tree(cname, subclasses[cname], 0)
+ for c in self.queue:
+ if parents[c.name] == 0:
+ self._do_print_tree(c.name, subclasses[c.name], 0)
def print_index(self):
"""
@@ -406,8 +409,8 @@ class HtmlExporter(object):
print >>self.file, " :maxdepth: 1"
print >>self.file, ""
# print all top classes
- for cname in self.queue:
- print >>self.file, " " + cname
+ for c in self.queue:
+ print >>self.file, " " + c.name
def print_tree_rst(self, header, footer):
@@ -431,8 +434,7 @@ class HtmlExporter(object):
self.file.close()
description = """
-Generate RST documentation for given CIM classes. The tool connects to specified CIMOM
-and reads class definition from there. It generates separate RST file for each class
+Generate RST documentation from given MOF files. It generates separate RST file for each class
specified on command line and for all it's parents.
The tool also generates tree.rst with inheritance tree and index.rst with table of content.
@@ -444,17 +446,26 @@ Use Sphinx to generate html documentation from the RST files.
"""
parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description)
-parser.add_option('-u', '--url', action='store', dest='addr', default='https://localhost:5989', help='URL of CIM server, default: https://localhost:5989')
-parser.add_option('-U', '--user', action='store', dest='user', default=None, help='CIM user name')
-parser.add_option('-P', '--password', action='store', dest='password', default=None, help='CIM password')
+parser.add_option('-M', '--mof', action='append', dest='mof', default=[], help='MOF files to generate documentation from, can be used multiple times')
+parser.add_option('-S', '--schema', action='append', dest='schema', default=[], help='MOF files to scan for dependencies, can be used multiple times')
parser.add_option('-H', '--header', action='store', dest='header', default=None, help='File with HTML page header')
parser.add_option('-F', '--footer', action='store', dest='footer', default=None, help='File with HTML page footer')
(options, args) = parser.parse_args()
-cliconn = pywbem.WBEMConnection(options.addr, (options.user, options.password))
-exporter = HtmlExporter(cliconn)
-for c in args:
- exporter.add_class(c)
-exporter.export(options.header, options.footer)
+exporter = HtmlExporter()
+
+for mof in options.schema:
+ konkretmof.MOF_add_include_path(os.path.dirname(mof))
+ konkretmof.MOF_parse_file(mof)
+
+for mof in options.mof:
+ konkretmof.MOF_parse_file(mof)
+for cls in konkretmof.MOF_Class_Decl.list.fget():
+ if cls.file_name in options.mof:
+ exporter.add_class(cls.name)
+for cls in args:
+ exporter.add_class(cls)
+
+exporter.export(options.header, options.footer)
diff --git a/tools/openlmi-doc-class2uml b/tools/openlmi-doc-class2uml
new file mode 100644
index 0000000..840ecfd
--- /dev/null
+++ b/tools/openlmi-doc-class2uml
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Jan Safranek <jsafrane@redhat.com>
+# Radek Novacek <rnovacek@redhat.com>
+#
+
+import os
+import konkretmof
+import optparse
+import re
+import cgi
+import sys
+
+class UmlExporter(object):
+
+ def __init__(self):
+ self.classcache = {}
+ # original list of classes
+ self.classes = set()
+ self.file = sys.stdout
+
+ def get_class(self, cls):
+ c = konkretmof.MOF_Class_Decl.list.fget().find(cls)
+ if c is None:
+ raise Exception("No such class: %s" % cls)
+ return c
+
+ def display_type(self, param):
+ """
+ Return displayable type of given parameter.
+ It adds [] if it's array and class name of referenced classes.
+ """
+ if isinstance(param, konkretmof.MOF_Reference_Decl):
+ ptype = param.class_name
+ elif isinstance(param, konkretmof.MOF_Parameter) and (param.data_type == konkretmof.TOK_REF):
+ ptype = param.ref_name
+ else:
+ ptype = param.type_name()
+
+ if isinstance(param, (konkretmof.MOF_Property_Decl, konkretmof.MOF_Parameter)) and param.array_index != 0:
+ ptype = ptype + "[]"
+ return ptype
+
+ def print_class(self, c, display_local = True, box_only = False):
+ """
+ Print one class, inc. header.
+ """
+ parent = None
+
+ if c.super_class:
+ # draw arrow to parent
+ print >>self.file, "%s -down-|> %s" % (c.name, c.super_class.name)
+
+ if box_only:
+ self.file.write("class %s\n\n" % c.name)
+ return
+
+ self.file.write("class %s {\n" % c.name)
+
+ if display_local:
+ for prop in sorted(c.properties().values(), key=lambda x: x.name):
+ self.file.write(" %s %s\n" % (self.display_type(prop), prop.name))
+
+
+ for m in sorted(c.methods().values(), key=lambda x: x.name):
+ self.file.write(" %s()\n" % (m.name))
+
+ self.file.write("}\n")
+ self.file.write("url of %s is [[%s.html]]\n" % (c.name, c.name))
+
+ def add_class(self, classname):
+ self.classes.add(classname)
+
+ def export(self, shrink, noassoc = False):
+ """
+ Print all classes and their parents.
+ """
+ print >>self.file, "@startuml"
+ while self.classes:
+ c = self.classes.pop()
+
+ cl = self.get_class(c)
+ if noassoc and cl.qualifiers.get("Association", False):
+ continue
+
+ if shrink and shrink.match(c):
+ self.print_class(cl, box_only = True)
+ else:
+ self.print_class(cl)
+
+ print >>self.file, "@enduml"
+
+description = """
+Generate UML image for given classes. Each class specified on command line
+will be drawn as one box, containing locally defined or re-defined properties
+and methods. Inheritance will be shown as arrow between a parent class and a
+subclass.
+
+The generated file can be coverted to a picture by PlantUML tool.
+"""
+
+parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description)
+parser.add_option('-M', '--mof', action='append', dest='mof', default=[], help='MOF files to generate documentation from, can be used multiple times')
+parser.add_option('-S', '--schema', action='append', dest='schema', default=[], help='MOF files to scan for dependencies, can be used multiple times')
+parser.add_option('-s', '--shrink', action='store', dest='shrink', default=None, help='Regular expression pattern of CIM classes, which will be drawn only as boxes, without properties.')
+parser.add_option('-A', '--no-associations', action='store_true', dest='noassoc', default=False, help='Skip association classes.')
+(options, args) = parser.parse_args()
+
+sys.stdout.softspace=0
+
+shrink = None
+if options.shrink:
+ shrink = re.compile(options.shrink)
+
+for mof in options.schema:
+ konkretmof.MOF_add_include_path(os.path.dirname(mof))
+ konkretmof.MOF_parse_file(mof)
+
+for mof in options.mof:
+ konkretmof.MOF_parse_file(mof)
+
+exporter = UmlExporter()
+
+for c in args:
+ exporter.add_class(c)
+
+exporter.export(shrink = shrink, noassoc = options.noassoc)