diff options
author | Michal Minar <miminar@redhat.com> | 2013-06-30 09:00:33 +0200 |
---|---|---|
committer | Michal Minar <miminar@redhat.com> | 2013-07-03 18:28:46 +0200 |
commit | 9385cf6d59be89d81bb137d77907c7bb4699bdf6 (patch) | |
tree | 448ceda7cb5da57112ec0211c6fa42df4133f8ad /src/software/test/test_software_identity_file_check.py | |
parent | 23dc511a8dd2d6e487f1bb04f880b17ee4405ad7 (diff) | |
download | openlmi-providers-9385cf6d59be89d81bb137d77907c7bb4699bdf6.tar.gz openlmi-providers-9385cf6d59be89d81bb137d77907c7bb4699bdf6.tar.xz openlmi-providers-9385cf6d59be89d81bb137d77907c7bb4699bdf6.zip |
added tests for SoftwareIdentityFileCheck
Diffstat (limited to 'src/software/test/test_software_identity_file_check.py')
-rwxr-xr-x | src/software/test/test_software_identity_file_check.py | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/src/software/test/test_software_identity_file_check.py b/src/software/test/test_software_identity_file_check.py new file mode 100755 index 0000000..ab7eb58 --- /dev/null +++ b/src/software/test/test_software_identity_file_check.py @@ -0,0 +1,431 @@ +#!/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 os +import pywbem +import re +import socket +import subprocess +import unittest + +import base +import rpmcache + +RE_CHECKSUM = re.compile(r'^([0-9a-fA-F]+)\s+.*') + +class TestSoftwareIdentityFileCheck( + base.SoftwareBaseTestCase): #pylint: disable=R0904 + """ + Basic cim operations test. + """ + + CLASS_NAME = "LMI_SoftwareIdentityFileCheck" + KEYS = ("CheckID", "Name", "SoftwareElementID", "SoftwareElementState") + + # maps algorithm id to the shell command + HASH_COMMAND = { + 1 : "md5sum", + 2 : "sha1sum", + 8 : "sha256sum", + 9 : "sha384sum", + 10 : "sha512sum", + 11 : "sha224sum" + } + + # maps algorithm id to the length of digest string + HASH_DIGEST_LENGTH = { + 1 : 32, #MD5 + 2 : 40, #SHA1 + 8 : 64, #SHA256 + 9 : 96, #SHA384 + 10 : 128, #SHA512 + 11 : 56 #SHA224 + } + + @classmethod + def needs_pkgdb_files(cls): + return True + + def make_checksum_str(self, csumnum, filename): + """ + @return checksum of installed file + """ + return RE_CHECKSUM.match(subprocess.check_output([ + self.HASH_COMMAND[csumnum], filename])).group(1).lower() + + def make_op(self, pkg, file_name, newer=True): + """ + :rtype: (``pywbem.CIMInstanceName``) object path of + SoftwareIdentityFileCheck + """ + objpath = self.objpath.copy() + objpath["CheckID"] = 'LMI:LMI_SoftwareIdentityFileCheck' + objpath["Name"] = file_name + objpath["SoftwareElementID"] = pkg.get_nevra( + newer=newer, with_epoch="ALWAYS") + objpath["SoftwareElementState"] = pywbem.Uint16(2) #Executable + objpath["TargetOperatingSystem"] = pywbem.Uint16(36) #LINUX + objpath["Version"] = getattr(pkg, 'up_evra' if newer else 'evra') + return objpath + + def do_check_symlink(self, pkg, filepath, inst, safe=False): + """ + Assert some details about symlink. + :param safe: (``bool``) If True, no modification is done to file. + """ + 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["UserID"], + "Unexpected uid of symlink for %s:%s"%(pkg.name, filepath)) + self.assertEqual(stats.st_gid, inst["GroupID"], + "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(int(stats.st_mtime), inst["LastModificationTime"], + "Unexpected mtime of symlink for %s:%s"%(pkg.name, filepath)) + self.assertIsNone(inst["Checksum"], + "Checksum should be None for symlink %s:%s"%( + pkg.name, filepath)) + self.assertIsNone(inst["FileChecksum"], + "FileChecksum should be None for symlink %s:%s"%( + pkg.name, filepath)) + self.assertIsNone(inst["MD5Checksum"], + "MD5Checksum should be None for symlink %s:%s"%( + pkg.name, filepath)) + + if safe: + return + + # modify owner + prev_user = inst["UserID"] + os.lchown(filepath, stats.st_uid + 1, -1) + inst = self.conn.GetInstance(InstanceName=inst.path) + self.assertEqual(inst["UserIDOriginal"] + 1, inst["UserID"], + "Unexpected uid of modified symlink for %s:%s"%( + pkg.name, filepath)) + self.assertEqual(prev_user + 1, inst["UserID"], + "Unexpected uid of modified symlink for %s:%s"%( + pkg.name, filepath)) + self.assertEqual(stats.st_gid, inst["GroupID"], + "Unexpected gid of modified symlink for %s:%s"%( + pkg.name, filepath)) + self.assertEqual([ + pywbem.Uint16(7) #UserID + ], inst["FailedFlags"]) + + # modify link_target + os.remove(filepath) + lt_modif = "wrong" + "*"*len(inst["LinkTargetOriginal"]) + os.symlink(lt_modif, filepath) + os.lchown(filepath, inst["UserIDOriginal"], inst["GroupIDOriginal"]) + + inst = self.conn.GetInstance(InstanceName=inst.path) + self.assertEqual(set([ + pywbem.Uint16(1), #FileSize + pywbem.Uint16(6), #LinkTarget + ]), set(inst["FailedFlags"])) + self.assertGreater(len(inst["LinkTarget"]), + len(inst["LinkTargetOriginal"])) + + self.assertTrue(inst["FileExists"], + "File %s:%s should exist"%(pkg.name, filepath)) + self.assertEqual(inst["FileTypeOriginal"], inst["FileType"], + "File type should match for symlink %s:%s"%(pkg.name, filepath)) + self.assertNotEqual(inst["FileSizeOriginal"], inst["FileSize"], + "File size should not match for symlink %s:%s"%( + pkg.name, filepath)) + self.assertEqual(inst["FileModeOriginal"], inst["FileMode"], + "File mode should match for symlink %s:%s"%(pkg.name, filepath)) + self.assertEqual(lt_modif, inst["LinkTarget"], + "Link target should match modified path %s:%s"%( + pkg.name, filepath)) + self.assertNotEqual(inst["LinkTargetOriginal"], inst["ListTarget"], + "Link target should not match for symlink %s:%s"%( + pkg.name, filepath)) + self.assertEqual(inst["UserIDOriginal"], inst["UserID"], + "File uid should match for symlink %s:%s"%(pkg.name, filepath)) + self.assertEqual(inst["GroupIDOriginal"], inst["GroupID"], + "File gid 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["UserID"], + "Unexpected uid for directory %s:%s"%(pkg.name, filepath)) + self.assertEqual(stats.st_gid, inst["GroupID"], + "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.assertIsNone(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)) + self.assertEqual([], inst["FailedFlags"]) + + def do_check_file(self, pkg, filepath, inst, safe=False): + """ + Assert some details about regurar file. + :param safe: (``bool``) If True, no modification is done to 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["UserID"], + "Unexpected file uid for %s:%s"%(pkg.name, filepath)) + self.assertEqual(stats.st_gid, inst["GroupID"], + "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['ChecksumType'], filepath) + self.assertEqual(csum, inst["FileChecksum"].lower(), + "Unexpected checksum for reqular file %s:%s"%(pkg.name, filepath)) + self.assertEqual(inst["LastModificationTime"], + inst["LastModificationTimeOriginal"], + "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)) + + if safe: + return + + # make it longer + with open(filepath, "a+") as fobj: + fobj.write("data\n") + inst = self.conn.GetInstance(InstanceName=inst.path) + self.assertEqual(set([ + pywbem.Uint16(1), #FileSize + pywbem.Uint16(3), #Checksum + pywbem.Uint16(9), #LastModificationTime + ]), set(inst["FailedFlags"])) + + self.assertGreater(inst["FileSize"], inst["FileSizeOriginal"], + "File size should be greater, then expected for reqular file" + " %s:%s"%(pkg.name, filepath)) + self.assertGreater(inst["LastModificationTime"], + inst["LastModificationTimeOriginal"], + "Unexpected mtime for reqular file %s:%s"%(pkg.name, filepath)) + + self.assertTrue(inst["FileExists"], + "Regular file should exist %s:%s"%(pkg.name, filepath)) + + # change file type + os.remove(filepath) + os.symlink(filepath, filepath) + os.lchown(filepath, inst["UserIDOriginal"], + inst["GroupIDOriginal"]) + inst = self.conn.GetInstance(InstanceName=inst.path) + self.assertNotEqual(inst["LinkTargetOriginal"], inst["LinkTarget"], + "Link target should not match for %s:%s"%(pkg.name, filepath)) + self.assertNotEqual(inst["FileSizeOriginal"], inst["FileSize"], + "File size should not match for %s:%s"%(pkg.name, filepath)) + self.assertGreater(inst["LastModificationTime"], + inst["LastModificationTimeOriginal"], + "File mtime should be greater than expected for %s:%s"%( + pkg.name, filepath)) + self.assertNotEqual(inst["FileTypeOriginal"], 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.assertIn(pywbem.Uint16(2), #FileMode + inst["FailedFlags"]) + + # remove it + os.remove(filepath) + inst = self.conn.GetInstance(InstanceName=inst.path) + self.assertEqual(inst["LinkTargetOriginal"], inst["LinkTarget"], + "Link target does not match for regular file %s:%s"%( + pkg.name, filepath)) + self.assertNotEqual(inst["FileSizeOriginal"], 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["UserID"]) + self.assertIsNone(inst["GroupID"]) + self.assertEqual([ + pywbem.Uint16(0), #exists + ], inst["FileExists"]) + + def check_filepath(self, pkg, filepath, safe=False): + """ + Make a check of particular file of package. + All files are expected to have no flaw. + + :param safe: (``bool``) says, whether modifications to file can be done + """ + objpath = self.make_op(pkg, filepath) + inst = self.conn.GetInstance(InstanceName=objpath) + 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() == "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(0, len(inst["FailedFlags"]), + "FailedFlags must be empty") + + for prop in ( "FileType", "UserID", "GroupID" + , "FileMode", "FileSize", "LinkTarget" + , "FileChecksum", "FileModeFlags"): + if ( ( os.path.islink(filepath) + or (not os.path.isfile(filepath))) + and prop == "FileSize"): + continue + self.assertEqual(inst[prop+"Original"], inst[prop], + "%s should match for %s:%s"%(prop, pkg.name, filepath)) + if os.path.islink(filepath): + self.do_check_symlink(pkg, filepath, inst, safe=safe) + elif os.path.isdir(filepath): + self.do_check_directory(pkg, filepath, inst) + elif os.path.isfile(filepath): + self.do_check_file(pkg, filepath, inst, safe=safe) + + def test_get_instance_safe(self): + """ + Tests GetInstance call. + """ + for pkg in self.safe_pkgs: + files = self.pkgdb_files[pkg.name] + for filepath in files: + self.check_filepath(pkg, filepath, safe=True) + + def test_enum_instance_names(self): + """ + Tests EnumInstanceNames - which should not be supported. + """ + self.assertRaisesCIM(pywbem.CIM_ERR_NOT_SUPPORTED, + self.conn.EnumerateInstanceNames, ClassName=self.CLASS_NAME) + + def test_invoke_method_safe(self): + """ + Test Invoke method in a safe manner. + """ + for pkg in self.safe_pkgs: + files = self.pkgdb_files[pkg.name] + for filepath in files: + objpath = self.make_op(pkg, filepath) + + (rval, _) = self.conn.InvokeMethod( + MethodName="Invoke", + ObjectName=objpath) + self.assertEqual(pywbem.Uint32(0), #Satisfied + rval, + msg="Invoke method should be successful for %s:%s"%( + pkg.name, filepath)) + + (rval, _) = self.conn.InvokeMethod( + MethodName="InvokeOnSystem", + ObjectName=objpath, + TargetSystem=pywbem.CIMInstanceName( + namespace="root/cimv2", + classname="Linux_ComputerSystem", + keybindings=pywbem.NocaseDict({ + "CreationClassName" : "Linux_ComputerSystem", + "Name" : socket.gethostname()}))) + + self.assertEqual(pywbem.Uint32(0), #Satisfied + rval, msg="InvokeOnSystem method should be successful" + " for %s:%s"%(pkg.name, filepath)) + + self.assertRaisesCIM(pywbem.CIM_ERR_NOT_FOUND, + self.conn.InvokeMethod, + MethodName="InvokeOnSystem", + ObjectName=objpath, + TargetSystem=pywbem.CIMInstanceName( + namespace="root/cimv2", + classname="Linux_ComputerSystem", + keybindings=pywbem.NocaseDict({ + "CreationClassName" : "Bad class name", + "Name" : "some random name"}))) + + @base.mark_dangerous + def test_invoke_method(self): + """ + Tests Invoke method invocation. + """ + for pkg in self.dangerous_pkgs: + if not rpmcache.is_pkg_installed(pkg.name): + rpmcache.install_pkg(pkg.name) + for filepath in self.pkgdb_files[pkg.name]: + objpath = self.make_op(pkg, filepath) + + (rval, _) = self.conn.InvokeMethod( + MethodName="Invoke", + ObjectName=objpath) + self.assertEqual(pywbem.Uint32(0) #Satisfied + , rval, + msg="Invoke method 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), #Not Satisfied + rval, + "Invoke method should not pass for modified file %s:%s"%( + pkg.name, filepath)) + +def suite(): + """For unittest loaders.""" + return unittest.TestLoader().loadTestsFromTestCase( + TestSoftwareIdentityFileCheck) + +if __name__ == '__main__': + unittest.main() |