OLD | NEW |
1 """ | 1 """ |
2 KVM test utility functions. | 2 KVM test utility functions. |
3 | 3 |
4 @copyright: 2008-2009 Red Hat Inc. | 4 @copyright: 2008-2009 Red Hat Inc. |
5 """ | 5 """ |
6 | 6 |
7 import time, string, random, socket, os, signal, re, logging, commands, cPickle | 7 import time, string, random, socket, os, signal, re, logging, commands, cPickle |
8 import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict | 8 import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict |
| 9 import inspect |
9 from autotest_lib.client.bin import utils, os_dep | 10 from autotest_lib.client.bin import utils, os_dep |
10 from autotest_lib.client.common_lib import error, logging_config | 11 from autotest_lib.client.common_lib import error, logging_config |
11 import kvm_subprocess | 12 import kvm_subprocess |
12 try: | 13 try: |
13 import koji | 14 import koji |
14 KOJI_INSTALLED = True | 15 KOJI_INSTALLED = True |
15 except ImportError: | 16 except ImportError: |
16 KOJI_INSTALLED = False | 17 KOJI_INSTALLED = False |
17 | 18 |
18 | 19 |
(...skipping 649 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
668 return remote_login(client, host, port, username, password, prompt, | 669 return remote_login(client, host, port, username, password, prompt, |
669 linesep, log_filename, internal_timeout) | 670 linesep, log_filename, internal_timeout) |
670 except LoginError, e: | 671 except LoginError, e: |
671 logging.debug(e) | 672 logging.debug(e) |
672 time.sleep(2) | 673 time.sleep(2) |
673 # Timeout expired; try one more time but don't catch exceptions | 674 # Timeout expired; try one more time but don't catch exceptions |
674 return remote_login(client, host, port, username, password, prompt, | 675 return remote_login(client, host, port, username, password, prompt, |
675 linesep, log_filename, internal_timeout) | 676 linesep, log_filename, internal_timeout) |
676 | 677 |
677 | 678 |
678 def _remote_scp(session, password, transfer_timeout=600, login_timeout=10): | 679 def _remote_scp(session, password_list, transfer_timeout=600, login_timeout=10): |
679 """ | 680 """ |
680 Transfer file(s) to a remote host (guest) using SCP. Wait for questions | 681 Transfer file(s) to a remote host (guest) using SCP. Wait for questions |
681 and provide answers. If login_timeout expires while waiting for output | 682 and provide answers. If login_timeout expires while waiting for output |
682 from the child (e.g. a password prompt), fail. If transfer_timeout expires | 683 from the child (e.g. a password prompt), fail. If transfer_timeout expires |
683 while waiting for the transfer to complete, fail. | 684 while waiting for the transfer to complete, fail. |
684 | 685 |
685 @brief: Transfer files using SCP, given a command line. | 686 @brief: Transfer files using SCP, given a command line. |
686 | 687 |
687 @param session: An Expect or ShellSession instance to operate on | 688 @param session: An Expect or ShellSession instance to operate on |
688 @param password: The password to send in reply to a password prompt. | 689 @param password_list: Password list to send in reply to the password prompt |
689 @param transfer_timeout: The time duration (in seconds) to wait for the | 690 @param transfer_timeout: The time duration (in seconds) to wait for the |
690 transfer to complete. | 691 transfer to complete. |
691 @param login_timeout: The maximal time duration (in seconds) to wait for | 692 @param login_timeout: The maximal time duration (in seconds) to wait for |
692 each step of the login procedure (i.e. the "Are you sure" prompt or | 693 each step of the login procedure (i.e. the "Are you sure" prompt or |
693 the password prompt) | 694 the password prompt) |
694 @raise SCPAuthenticationError: If authentication fails | 695 @raise SCPAuthenticationError: If authentication fails |
695 @raise SCPTransferTimeoutError: If the transfer fails to complete in time | 696 @raise SCPTransferTimeoutError: If the transfer fails to complete in time |
696 @raise SCPTransferFailedError: If the process terminates with a nonzero | 697 @raise SCPTransferFailedError: If the process terminates with a nonzero |
697 exit code | 698 exit code |
698 @raise SCPError: If some other error occurs | 699 @raise SCPError: If some other error occurs |
699 """ | 700 """ |
700 password_prompt_count = 0 | 701 password_prompt_count = 0 |
701 timeout = login_timeout | 702 timeout = login_timeout |
702 authentication_done = False | 703 authentication_done = False |
703 | 704 |
| 705 scp_type = len(password_list) |
| 706 |
704 while True: | 707 while True: |
705 try: | 708 try: |
706 match, text = session.read_until_last_line_matches( | 709 match, text = session.read_until_last_line_matches( |
707 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], | 710 [r"[Aa]re you sure", r"[Pp]assword:\s*$", r"lost connection"], |
708 timeout=timeout, internal_timeout=0.5) | 711 timeout=timeout, internal_timeout=0.5) |
709 if match == 0: # "Are you sure you want to continue connecting" | 712 if match == 0: # "Are you sure you want to continue connecting" |
710 logging.debug("Got 'Are you sure...'; sending 'yes'") | 713 logging.debug("Got 'Are you sure...'; sending 'yes'") |
711 session.sendline("yes") | 714 session.sendline("yes") |
712 continue | 715 continue |
713 elif match == 1: # "password:" | 716 elif match == 1: # "password:" |
714 if password_prompt_count == 0: | 717 if password_prompt_count == 0: |
715 logging.debug("Got password prompt; sending '%s'", password) | 718 logging.debug("Got password prompt; sending '%s'" % |
716 session.sendline(password) | 719 password_list[password_prompt_count]) |
| 720 session.sendline(password_list[password_prompt_count]) |
| 721 password_prompt_count += 1 |
| 722 timeout = transfer_timeout |
| 723 if scp_type == 1: |
| 724 authentication_done = True |
| 725 continue |
| 726 elif password_prompt_count == 1 and scp_type == 2: |
| 727 logging.debug("Got password prompt; sending '%s'" % |
| 728 password_list[password_prompt_count]) |
| 729 session.sendline(password_list[password_prompt_count]) |
717 password_prompt_count += 1 | 730 password_prompt_count += 1 |
718 timeout = transfer_timeout | 731 timeout = transfer_timeout |
719 authentication_done = True | 732 authentication_done = True |
720 continue | 733 continue |
721 else: | 734 else: |
722 raise SCPAuthenticationError("Got password prompt twice", | 735 raise SCPAuthenticationError("Got password prompt twice", |
723 text) | 736 text) |
724 elif match == 2: # "lost connection" | 737 elif match == 2: # "lost connection" |
725 raise SCPError("SCP client said 'lost connection'", text) | 738 raise SCPError("SCP client said 'lost connection'", text) |
726 except kvm_subprocess.ExpectTimeoutError, e: | 739 except kvm_subprocess.ExpectTimeoutError, e: |
727 if authentication_done: | 740 if authentication_done: |
728 raise SCPTransferTimeoutError(e.output) | 741 raise SCPTransferTimeoutError(e.output) |
729 else: | 742 else: |
730 raise SCPAuthenticationTimeoutError(e.output) | 743 raise SCPAuthenticationTimeoutError(e.output) |
731 except kvm_subprocess.ExpectProcessTerminatedError, e: | 744 except kvm_subprocess.ExpectProcessTerminatedError, e: |
732 if e.status == 0: | 745 if e.status == 0: |
733 logging.debug("SCP process terminated with status 0") | 746 logging.debug("SCP process terminated with status 0") |
734 break | 747 break |
735 else: | 748 else: |
736 raise SCPTransferFailedError(e.status, e.output) | 749 raise SCPTransferFailedError(e.status, e.output) |
737 | 750 |
738 | 751 |
739 def remote_scp(command, password, log_filename=None, transfer_timeout=600, | 752 def remote_scp(command, password_list, log_filename=None, transfer_timeout=600, |
740 login_timeout=10): | 753 login_timeout=10): |
741 """ | 754 """ |
742 Transfer file(s) to a remote host (guest) using SCP. | 755 Transfer file(s) to a remote host (guest) using SCP. |
743 | 756 |
744 @brief: Transfer files using SCP, given a command line. | 757 @brief: Transfer files using SCP, given a command line. |
745 | 758 |
746 @param command: The command to execute | 759 @param command: The command to execute |
747 (e.g. "scp -r foobar root@localhost:/tmp/"). | 760 (e.g. "scp -r foobar root@localhost:/tmp/"). |
748 @param password: The password to send in reply to a password prompt. | 761 @param password_list: Password list to send in reply to a password prompt. |
749 @param log_filename: If specified, log all output to this file | 762 @param log_filename: If specified, log all output to this file |
750 @param transfer_timeout: The time duration (in seconds) to wait for the | 763 @param transfer_timeout: The time duration (in seconds) to wait for the |
751 transfer to complete. | 764 transfer to complete. |
752 @param login_timeout: The maximal time duration (in seconds) to wait for | 765 @param login_timeout: The maximal time duration (in seconds) to wait for |
753 each step of the login procedure (i.e. the "Are you sure" prompt | 766 each step of the login procedure (i.e. the "Are you sure" prompt |
754 or the password prompt) | 767 or the password prompt) |
755 @raise: Whatever _remote_scp() raises | 768 @raise: Whatever _remote_scp() raises |
756 """ | 769 """ |
757 logging.debug("Trying to SCP with command '%s', timeout %ss", | 770 logging.debug("Trying to SCP with command '%s', timeout %ss", |
758 command, transfer_timeout) | 771 command, transfer_timeout) |
759 if log_filename: | 772 if log_filename: |
760 output_func = log_line | 773 output_func = log_line |
761 output_params = (log_filename,) | 774 output_params = (log_filename,) |
762 else: | 775 else: |
763 output_func = None | 776 output_func = None |
764 output_params = () | 777 output_params = () |
765 session = kvm_subprocess.Expect(command, | 778 session = kvm_subprocess.Expect(command, |
766 output_func=output_func, | 779 output_func=output_func, |
767 output_params=output_params) | 780 output_params=output_params) |
768 try: | 781 try: |
769 _remote_scp(session, password, transfer_timeout, login_timeout) | 782 _remote_scp(session, password_list, transfer_timeout, login_timeout) |
770 finally: | 783 finally: |
771 session.close() | 784 session.close() |
772 | 785 |
773 | 786 |
774 def scp_to_remote(host, port, username, password, local_path, remote_path, | 787 def scp_to_remote(host, port, username, password, local_path, remote_path, |
775 log_filename=None, timeout=600): | 788 log_filename=None, timeout=600): |
776 """ | 789 """ |
777 Copy files to a remote host (guest) through scp. | 790 Copy files to a remote host (guest) through scp. |
778 | 791 |
779 @param host: Hostname or IP address | 792 @param host: Hostname or IP address |
780 @param username: Username (if required) | 793 @param username: Username (if required) |
781 @param password: Password (if required) | 794 @param password: Password (if required) |
782 @param local_path: Path on the local machine where we are copying from | 795 @param local_path: Path on the local machine where we are copying from |
783 @param remote_path: Path on the remote machine where we are copying to | 796 @param remote_path: Path on the remote machine where we are copying to |
784 @param log_filename: If specified, log all output to this file | 797 @param log_filename: If specified, log all output to this file |
785 @param timeout: The time duration (in seconds) to wait for the transfer | 798 @param timeout: The time duration (in seconds) to wait for the transfer |
786 to complete. | 799 to complete. |
787 @raise: Whatever remote_scp() raises | 800 @raise: Whatever remote_scp() raises |
788 """ | 801 """ |
789 command = ("scp -v -o UserKnownHostsFile=/dev/null " | 802 command = ("scp -v -o UserKnownHostsFile=/dev/null " |
790 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" % | 803 "-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" % |
791 (port, local_path, username, host, remote_path)) | 804 (port, local_path, username, host, remote_path)) |
792 remote_scp(command, password, log_filename, timeout) | 805 password_list = [] |
| 806 password_list.append(password) |
| 807 return remote_scp(command, password_list, log_filename, timeout) |
| 808 |
793 | 809 |
794 | 810 |
795 def scp_from_remote(host, port, username, password, remote_path, local_path, | 811 def scp_from_remote(host, port, username, password, remote_path, local_path, |
796 log_filename=None, timeout=600): | 812 log_filename=None, timeout=600): |
797 """ | 813 """ |
798 Copy files from a remote host (guest). | 814 Copy files from a remote host (guest). |
799 | 815 |
800 @param host: Hostname or IP address | 816 @param host: Hostname or IP address |
801 @param username: Username (if required) | 817 @param username: Username (if required) |
802 @param password: Password (if required) | 818 @param password: Password (if required) |
803 @param local_path: Path on the local machine where we are copying from | 819 @param local_path: Path on the local machine where we are copying from |
804 @param remote_path: Path on the remote machine where we are copying to | 820 @param remote_path: Path on the remote machine where we are copying to |
805 @param log_filename: If specified, log all output to this file | 821 @param log_filename: If specified, log all output to this file |
806 @param timeout: The time duration (in seconds) to wait for the transfer | 822 @param timeout: The time duration (in seconds) to wait for the transfer |
807 to complete. | 823 to complete. |
808 @raise: Whatever remote_scp() raises | 824 @raise: Whatever remote_scp() raises |
809 """ | 825 """ |
810 command = ("scp -v -o UserKnownHostsFile=/dev/null " | 826 command = ("scp -v -o UserKnownHostsFile=/dev/null " |
811 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" % | 827 "-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" % |
812 (port, username, host, remote_path, local_path)) | 828 (port, username, host, remote_path, local_path)) |
813 remote_scp(command, password, log_filename, timeout) | 829 password_list = [] |
| 830 password_list.append(password) |
| 831 remote_scp(command, password_list, log_filename, timeout) |
| 832 |
| 833 |
| 834 def scp_between_remotes(src, dst, port, s_passwd, d_passwd, s_name, d_name, |
| 835 s_path, d_path, log_filename=None, timeout=600): |
| 836 """ |
| 837 Copy files from a remote host (guest) to another remote host (guest). |
| 838 |
| 839 @param src/dst: Hostname or IP address of src and dst |
| 840 @param s_name/d_name: Username (if required) |
| 841 @param s_passwd/d_passwd: Password (if required) |
| 842 @param s_path/d_path: Path on the remote machine where we are copying |
| 843 from/to |
| 844 @param log_filename: If specified, log all output to this file |
| 845 @param timeout: The time duration (in seconds) to wait for the transfer |
| 846 to complete. |
| 847 |
| 848 @return: True on success and False on failure. |
| 849 """ |
| 850 command = ("scp -v -o UserKnownHostsFile=/dev/null -o " |
| 851 "PreferredAuthentications=password -r -P %s %s@%s:%s %s@%s:%s" % |
| 852 (port, s_name, src, s_path, d_name, dst, d_path)) |
| 853 password_list = [] |
| 854 password_list.append(s_passwd) |
| 855 password_list.append(d_passwd) |
| 856 return remote_scp(command, password_list, log_filename, timeout) |
814 | 857 |
815 | 858 |
816 def copy_files_to(address, client, username, password, port, local_path, | 859 def copy_files_to(address, client, username, password, port, local_path, |
817 remote_path, log_filename=None, verbose=False, timeout=600): | 860 remote_path, log_filename=None, verbose=False, timeout=600): |
818 """ | 861 """ |
819 Copy files to a remote host (guest) using the selected client. | 862 Copy files to a remote host (guest) using the selected client. |
820 | 863 |
821 @param client: Type of transfer client | 864 @param client: Type of transfer client |
822 @param username: Username (if required) | 865 @param username: Username (if required) |
823 @param password: Password (if requried) | 866 @param password: Password (if requried) |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1101 failed = False | 1144 failed = False |
1102 | 1145 |
1103 for dict in parser.get_dicts(): | 1146 for dict in parser.get_dicts(): |
1104 if dict.get("skip") == "yes": | 1147 if dict.get("skip") == "yes": |
1105 continue | 1148 continue |
1106 dependencies_satisfied = True | 1149 dependencies_satisfied = True |
1107 for dep in dict.get("dep"): | 1150 for dep in dict.get("dep"): |
1108 for test_name in status_dict.keys(): | 1151 for test_name in status_dict.keys(): |
1109 if not dep in test_name: | 1152 if not dep in test_name: |
1110 continue | 1153 continue |
1111 if not status_dict[test_name]: | 1154 # So the only really non-fatal state is WARN, |
| 1155 # All the others make it not safe to proceed with dependency |
| 1156 # execution |
| 1157 if status_dict[test_name] not in ['GOOD', 'WARN']: |
1112 dependencies_satisfied = False | 1158 dependencies_satisfied = False |
1113 break | 1159 break |
| 1160 test_iterations = int(dict.get("iterations", 1)) |
| 1161 test_tag = dict.get("shortname") |
| 1162 |
1114 if dependencies_satisfied: | 1163 if dependencies_satisfied: |
1115 test_iterations = int(dict.get("iterations", 1)) | |
1116 test_tag = dict.get("shortname") | |
1117 # Setting up profilers during test execution. | 1164 # Setting up profilers during test execution. |
1118 profilers = dict.get("profilers", "").split() | 1165 profilers = dict.get("profilers", "").split() |
1119 for profiler in profilers: | 1166 for profiler in profilers: |
1120 job.profilers.add(profiler) | 1167 job.profilers.add(profiler) |
1121 | |
1122 # We need only one execution, profiled, hence we're passing | 1168 # We need only one execution, profiled, hence we're passing |
1123 # the profile_only parameter to job.run_test(). | 1169 # the profile_only parameter to job.run_test(). |
1124 current_status = job.run_test("kvm", params=dict, tag=test_tag, | 1170 profile_only = bool(profilers) or None |
1125 iterations=test_iterations, | 1171 current_status = job.run_test_detail("kvm", params=dict, |
1126 profile_only= bool(profilers) or None) | 1172 tag=test_tag, |
1127 | 1173 iterations=test_iterations, |
| 1174 profile_only=profile_only) |
1128 for profiler in profilers: | 1175 for profiler in profilers: |
1129 job.profilers.delete(profiler) | 1176 job.profilers.delete(profiler) |
| 1177 else: |
| 1178 # We will force the test to fail as TestNA during preprocessing |
| 1179 dict['dependency_failed'] = 'yes' |
| 1180 current_status = job.run_test_detail("kvm", params=dict, |
| 1181 tag=test_tag, |
| 1182 iterations=test_iterations) |
1130 | 1183 |
1131 if not current_status: | 1184 if not current_status: |
1132 failed = True | 1185 failed = True |
1133 else: | |
1134 current_status = False | |
1135 status_dict[dict.get("name")] = current_status | 1186 status_dict[dict.get("name")] = current_status |
1136 | 1187 |
1137 return not failed | 1188 return not failed |
1138 | 1189 |
1139 | 1190 |
1140 def create_report(report_dir, results_dir): | 1191 def create_report(report_dir, results_dir): |
1141 """ | 1192 """ |
1142 Creates a neatly arranged HTML results report in the results dir. | 1193 Creates a neatly arranged HTML results report in the results dir. |
1143 | 1194 |
1144 @param report_dir: Directory where the report script is located. | 1195 @param report_dir: Directory where the report script is located. |
1145 @param results_dir: Directory where the results will be output. | 1196 @param results_dir: Directory where the results will be output. |
1146 """ | 1197 """ |
1147 reporter = os.path.join(report_dir, 'html_report.py') | 1198 reporter = os.path.join(report_dir, 'html_report.py') |
1148 html_file = os.path.join(results_dir, 'results.html') | 1199 html_file = os.path.join(results_dir, 'results.html') |
1149 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file)) | 1200 os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file)) |
1150 | 1201 |
1151 | 1202 |
| 1203 def display_attributes(instance): |
| 1204 """ |
| 1205 Inspects a given class instance attributes and displays them, convenient |
| 1206 for debugging. |
| 1207 """ |
| 1208 logging.debug("Attributes set:") |
| 1209 for member in inspect.getmembers(instance): |
| 1210 name, value = member |
| 1211 attribute = getattr(instance, name) |
| 1212 if not (name.startswith("__") or callable(attribute) or not value): |
| 1213 logging.debug(" %s: %s", name, value) |
| 1214 |
| 1215 |
1152 def get_full_pci_id(pci_id): | 1216 def get_full_pci_id(pci_id): |
1153 """ | 1217 """ |
1154 Get full PCI ID of pci_id. | 1218 Get full PCI ID of pci_id. |
1155 | 1219 |
1156 @param pci_id: PCI ID of a device. | 1220 @param pci_id: PCI ID of a device. |
1157 """ | 1221 """ |
1158 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id | 1222 cmd = "lspci -D | awk '/%s/ {print $1}'" % pci_id |
1159 status, full_id = commands.getstatusoutput(cmd) | 1223 status, full_id = commands.getstatusoutput(cmd) |
1160 if status != 0: | 1224 if status != 0: |
1161 return None | 1225 return None |
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1528 try: | 1592 try: |
1529 for pci_id in self.dev_drivers: | 1593 for pci_id in self.dev_drivers: |
1530 if not self._release_dev(pci_id): | 1594 if not self._release_dev(pci_id): |
1531 logging.error("Failed to release device %s to host", pci_id) | 1595 logging.error("Failed to release device %s to host", pci_id) |
1532 else: | 1596 else: |
1533 logging.info("Released device %s successfully", pci_id) | 1597 logging.info("Released device %s successfully", pci_id) |
1534 except: | 1598 except: |
1535 return | 1599 return |
1536 | 1600 |
1537 | 1601 |
1538 class KojiDownloader(object): | 1602 class KojiClient(object): |
1539 """ | 1603 """ |
1540 Stablish a connection with the build system, either koji or brew. | 1604 Stablishes a connection with the build system, either koji or brew. |
1541 | 1605 |
1542 This class provides a convenience methods to retrieve packages hosted on | 1606 This class provides convenience methods to retrieve information on packages |
1543 the build system. | 1607 and the packages themselves hosted on the build system. Packages should be |
| 1608 specified in the KojiPgkSpec syntax. |
1544 """ | 1609 """ |
1545 def __init__(self, cmd): | 1610 |
| 1611 CMD_LOOKUP_ORDER = ['/usr/bin/brew', '/usr/bin/koji' ] |
| 1612 |
| 1613 CONFIG_MAP = {'/usr/bin/brew': '/etc/brewkoji.conf', |
| 1614 '/usr/bin/koji': '/etc/koji.conf'} |
| 1615 |
| 1616 |
| 1617 def __init__(self, cmd=None): |
1546 """ | 1618 """ |
1547 Verifies whether the system has koji or brew installed, then loads | 1619 Verifies whether the system has koji or brew installed, then loads |
1548 the configuration file that will be used to download the files. | 1620 the configuration file that will be used to download the files. |
1549 | 1621 |
1550 @param cmd: Command name, either 'brew' or 'koji'. It is important | 1622 @type cmd: string |
1551 to figure out the appropriate configuration used by the | 1623 @param cmd: Optional command name, either 'brew' or 'koji'. If not |
1552 downloader. | 1624 set, get_default_command() is used and to look for |
1553 @param dst_dir: Destination dir for the packages. | 1625 one of them. |
| 1626 @raise: ValueError |
1554 """ | 1627 """ |
1555 if not KOJI_INSTALLED: | 1628 if not KOJI_INSTALLED: |
1556 raise ValueError('No koji/brew installed on the machine') | 1629 raise ValueError('No koji/brew installed on the machine') |
1557 | 1630 |
1558 if os.path.isfile(cmd): | 1631 # Instance variables used by many methods |
1559 koji_cmd = cmd | 1632 self.command = None |
| 1633 self.config = None |
| 1634 self.config_options = {} |
| 1635 self.session = None |
| 1636 |
| 1637 # Set koji command or get default |
| 1638 if cmd is None: |
| 1639 self.command = self.get_default_command() |
1560 else: | 1640 else: |
1561 koji_cmd = os_dep.command(cmd) | 1641 self.command = cmd |
1562 | 1642 |
1563 logging.debug("Found %s as the buildsystem interface", koji_cmd) | 1643 # Check koji command |
1564 | 1644 if not self.is_command_valid(): |
1565 config_map = {'/usr/bin/koji': '/etc/koji.conf', | 1645 raise ValueError('Koji command "%s" is not valid' % self.command) |
1566 '/usr/bin/brew': '/etc/brewkoji.conf'} | 1646 |
1567 | 1647 # Assuming command is valid, set configuration file and read it |
1568 try: | 1648 self.config = self.CONFIG_MAP[self.command] |
1569 config_file = config_map[koji_cmd] | 1649 self.read_config() |
1570 except IndexError: | 1650 |
1571 raise ValueError('Could not find config file for %s' % koji_cmd) | 1651 # Setup koji session |
1572 | 1652 server_url = self.config_options['server'] |
1573 base_name = os.path.basename(koji_cmd) | 1653 session_options = self.get_session_options() |
1574 if os.access(config_file, os.F_OK): | 1654 self.session = koji.ClientSession(server_url, |
1575 f = open(config_file) | 1655 session_options) |
1576 config = ConfigParser.ConfigParser() | 1656 |
1577 config.readfp(f) | 1657 |
1578 f.close() | 1658 def read_config(self, check_is_valid=True): |
1579 else: | 1659 ''' |
1580 raise IOError('Configuration file %s missing or with wrong ' | 1660 Reads options from the Koji configuration file |
1581 'permissions' % config_file) | 1661 |
1582 | 1662 By default it checks if the koji configuration is valid |
1583 if config.has_section(base_name): | 1663 |
1584 self.koji_options = {} | 1664 @type check_valid: boolean |
1585 session_options = {} | 1665 @param check_valid: whether to include a check on the configuration |
1586 server = None | 1666 @raises: ValueError |
1587 for name, value in config.items(base_name): | 1667 @returns: None |
1588 if name in ('user', 'password', 'debug_xmlrpc', 'debug'): | 1668 ''' |
1589 session_options[name] = value | 1669 if check_is_valid: |
1590 self.koji_options[name] = value | 1670 if not self.is_config_valid(): |
1591 self.session = koji.ClientSession(self.koji_options['server'], | 1671 raise ValueError('Koji config "%s" is not valid' % self.config) |
1592 session_options) | 1672 |
1593 else: | 1673 config = ConfigParser.ConfigParser() |
1594 raise ValueError('Koji config file %s does not have a %s ' | 1674 config.read(self.config) |
1595 'session' % (config_file, base_name)) | 1675 |
1596 | 1676 basename = os.path.basename(self.command) |
1597 | 1677 for name, value in config.items(basename): |
1598 def get(self, src_package, dst_dir, rfilter=None, tag=None, build=None, | 1678 self.config_options[name] = value |
1599 arch=None): | 1679 |
1600 """ | 1680 |
1601 Download a list of packages from the build system. | 1681 def get_session_options(self): |
1602 | 1682 ''' |
1603 This will download all packages originated from source package [package] | 1683 Filter only options necessary for setting up a cobbler client session |
1604 with given [tag] or [build] for the architecture reported by the | 1684 |
1605 machine. | 1685 @returns: only the options used for session setup |
1606 | 1686 ''' |
1607 @param src_package: Source package name. | 1687 session_options = {} |
1608 @param dst_dir: Destination directory for the downloaded packages. | 1688 for name, value in self.config_options.items(): |
1609 @param rfilter: Regexp filter, only download the packages that match | 1689 if name in ('user', 'password', 'debug_xmlrpc', 'debug'): |
1610 that particular filter. | 1690 session_options[name] = value |
1611 @param tag: Build system tag. | 1691 return session_options |
1612 @param build: Build system ID. | 1692 |
1613 @param arch: Package arch. Useful when you want to download noarch | 1693 |
1614 packages. | 1694 def is_command_valid(self): |
1615 | 1695 ''' |
1616 @return: List of paths with the downloaded rpm packages. | 1696 Checks if the currently set koji command is valid |
1617 """ | 1697 |
1618 if build and build.isdigit(): | 1698 @returns: True or False |
1619 build = int(build) | 1699 ''' |
1620 | 1700 koji_command_ok = True |
1621 if tag and build: | 1701 |
1622 logging.info("Both tag and build parameters provided, ignoring tag " | 1702 if not os.path.isfile(self.command): |
1623 "parameter...") | 1703 logging.error('Koji command "%s" is not a regular file', |
1624 | 1704 self.command) |
1625 if not tag and not build: | 1705 koji_command_ok = False |
1626 raise ValueError("Koji install selected but neither koji_tag " | 1706 |
1627 "nor koji_build parameters provided. Please " | 1707 if not os.access(self.command, os.X_OK): |
1628 "provide an appropriate tag or build name.") | 1708 logging.warn('Koji command "%s" is not executable: this is ' |
1629 | 1709 'not fatal but indicates an unexpected situation', |
1630 if not build: | 1710 self.command) |
1631 builds = self.session.listTagged(tag, latest=True, inherit=True, | 1711 |
1632 package=src_package) | 1712 if not self.command in self.CONFIG_MAP.keys(): |
1633 if not builds: | 1713 logging.error('Koji command "%s" does not have a configuration ' |
1634 raise ValueError("Tag %s has no builds of %s" % (tag, | 1714 'file associated to it', self.command) |
1635 src_package)) | 1715 koji_command_ok = False |
1636 info = builds[0] | 1716 |
1637 else: | 1717 return koji_command_ok |
1638 info = self.session.getBuild(build) | 1718 |
1639 | 1719 |
1640 if info is None: | 1720 def is_config_valid(self): |
1641 raise ValueError('No such brew/koji build: %s' % build) | 1721 ''' |
1642 | 1722 Checks if the currently set koji configuration is valid |
| 1723 |
| 1724 @returns: True or False |
| 1725 ''' |
| 1726 koji_config_ok = True |
| 1727 |
| 1728 if not os.path.isfile(self.config): |
| 1729 logging.error('Koji config "%s" is not a regular file', self.config) |
| 1730 koji_config_ok = False |
| 1731 |
| 1732 if not os.access(self.config, os.R_OK): |
| 1733 logging.error('Koji config "%s" is not readable', self.config) |
| 1734 koji_config_ok = False |
| 1735 |
| 1736 config = ConfigParser.ConfigParser() |
| 1737 config.read(self.config) |
| 1738 basename = os.path.basename(self.command) |
| 1739 if not config.has_section(basename): |
| 1740 logging.error('Koji configuration file "%s" does not have a ' |
| 1741 'section "%s", named after the base name of the ' |
| 1742 'currently set koji command "%s"', self.config, |
| 1743 basename, self.command) |
| 1744 koji_config_ok = False |
| 1745 |
| 1746 return koji_config_ok |
| 1747 |
| 1748 |
| 1749 def get_default_command(self): |
| 1750 ''' |
| 1751 Looks up for koji or brew "binaries" on the system |
| 1752 |
| 1753 Systems with plain koji usually don't have a brew cmd, while systems |
| 1754 with koji, have *both* koji and brew utilities. So we look for brew |
| 1755 first, and if found, we consider that the system is configured for |
| 1756 brew. If not, we consider this is a system with plain koji. |
| 1757 |
| 1758 @returns: either koji or brew command line executable path, or None |
| 1759 ''' |
| 1760 koji_command = None |
| 1761 for command in self.CMD_LOOKUP_ORDER: |
| 1762 if os.path.isfile(command): |
| 1763 koji_command = command |
| 1764 break |
| 1765 else: |
| 1766 koji_command_basename = os.path.basename(koji_command) |
| 1767 try: |
| 1768 koji_command = os_dep.command(koji_command_basename) |
| 1769 break |
| 1770 except ValueError: |
| 1771 pass |
| 1772 return koji_command |
| 1773 |
| 1774 |
| 1775 def get_pkg_info(self, pkg): |
| 1776 ''' |
| 1777 Returns information from Koji on the package |
| 1778 |
| 1779 @type pkg: KojiPkgSpec |
| 1780 @param pkg: information about the package, as a KojiPkgSpec instance |
| 1781 |
| 1782 @returns: information from Koji about the specified package |
| 1783 ''' |
| 1784 info = {} |
| 1785 if pkg.build is not None: |
| 1786 info = self.session.getBuild(int(pkg.build)) |
| 1787 elif pkg.tag is not None and pkg.package is not None: |
| 1788 builds = self.session.listTagged(pkg.tag, |
| 1789 latest=True, |
| 1790 inherit=True, |
| 1791 package=pkg.package) |
| 1792 if builds: |
| 1793 info = builds[0] |
| 1794 return info |
| 1795 |
| 1796 |
| 1797 def is_pkg_valid(self, pkg): |
| 1798 ''' |
| 1799 Checks if this package is altogether valid on Koji |
| 1800 |
| 1801 This verifies if the build or tag specified in the package |
| 1802 specification actually exist on the Koji server |
| 1803 |
| 1804 @returns: True or False |
| 1805 ''' |
| 1806 valid = True |
| 1807 if not self.is_pkg_spec_build_valid(pkg): |
| 1808 valid = False |
| 1809 if not self.is_pkg_spec_tag_valid(pkg): |
| 1810 valid = False |
| 1811 return valid |
| 1812 |
| 1813 |
| 1814 def is_pkg_spec_build_valid(self, pkg): |
| 1815 ''' |
| 1816 Checks if build is valid on Koji |
| 1817 |
| 1818 @param pkg: a Pkg instance |
| 1819 ''' |
| 1820 if pkg.build is not None: |
| 1821 info = self.session.getBuild(int(pkg.build)) |
| 1822 if info: |
| 1823 return True |
| 1824 return False |
| 1825 |
| 1826 |
| 1827 def is_pkg_spec_tag_valid(self, pkg): |
| 1828 ''' |
| 1829 Checks if tag is valid on Koji |
| 1830 |
| 1831 @type pkg: KojiPkgSpec |
| 1832 @param pkg: a package specification |
| 1833 ''' |
| 1834 if pkg.tag is not None: |
| 1835 tag = self.session.getTag(pkg.tag) |
| 1836 if tag: |
| 1837 return True |
| 1838 return False |
| 1839 |
| 1840 |
| 1841 def get_pkg_rpm_info(self, pkg, arch=None): |
| 1842 ''' |
| 1843 Returns a list of infomation on the RPM packages found on koji |
| 1844 |
| 1845 @type pkg: KojiPkgSpec |
| 1846 @param pkg: a package specification |
| 1847 @type arch: string |
| 1848 @param arch: packages built for this architecture, but also including |
| 1849 architecture independent (noarch) packages |
| 1850 ''' |
1643 if arch is None: | 1851 if arch is None: |
1644 arch = utils.get_arch() | 1852 arch = utils.get_arch() |
1645 | 1853 rpms = [] |
1646 rpms = self.session.listRPMs(buildID=info['id'], | 1854 info = self.get_pkg_info(pkg) |
1647 arches=arch) | 1855 if info: |
1648 if not rpms: | 1856 rpms = self.session.listRPMs(buildID=info['id'], |
1649 raise ValueError("No %s packages available for %s" % | 1857 arches=[arch, 'noarch']) |
1650 arch, koji.buildLabel(info)) | 1858 if pkg.subpackages: |
1651 | 1859 rpms = [d for d in rpms if d['name'] in pkg.subpackages] |
1652 rpm_paths = [] | 1860 return rpms |
| 1861 |
| 1862 |
| 1863 def get_pkg_rpm_names(self, pkg, arch=None): |
| 1864 ''' |
| 1865 Gets the names for the RPM packages specified in pkg |
| 1866 |
| 1867 @type pkg: KojiPkgSpec |
| 1868 @param pkg: a package specification |
| 1869 @type arch: string |
| 1870 @param arch: packages built for this architecture, but also including |
| 1871 architecture independent (noarch) packages |
| 1872 ''' |
| 1873 if arch is None: |
| 1874 arch = utils.get_arch() |
| 1875 rpms = self.get_pkg_rpm_info(pkg, arch) |
| 1876 return [rpm['name'] for rpm in rpms] |
| 1877 |
| 1878 |
| 1879 def get_pkg_rpm_file_names(self, pkg, arch=None): |
| 1880 ''' |
| 1881 Gets the file names for the RPM packages specified in pkg |
| 1882 |
| 1883 @type pkg: KojiPkgSpec |
| 1884 @param pkg: a package specification |
| 1885 @type arch: string |
| 1886 @param arch: packages built for this architecture, but also including |
| 1887 architecture independent (noarch) packages |
| 1888 ''' |
| 1889 if arch is None: |
| 1890 arch = utils.get_arch() |
| 1891 rpm_names = [] |
| 1892 rpms = self.get_pkg_rpm_info(pkg, arch) |
| 1893 for rpm in rpms: |
| 1894 arch_rpm_name = koji.pathinfo.rpm(rpm) |
| 1895 rpm_name = os.path.basename(arch_rpm_name) |
| 1896 rpm_names.append(rpm_name) |
| 1897 return rpm_names |
| 1898 |
| 1899 |
| 1900 def get_pkg_urls(self, pkg, arch=None): |
| 1901 ''' |
| 1902 Gets the urls for the packages specified in pkg |
| 1903 |
| 1904 @type pkg: KojiPkgSpec |
| 1905 @param pkg: a package specification |
| 1906 @type arch: string |
| 1907 @param arch: packages built for this architecture, but also including |
| 1908 architecture independent (noarch) packages |
| 1909 ''' |
| 1910 info = self.get_pkg_info(pkg) |
| 1911 rpms = self.get_pkg_rpm_info(pkg, arch) |
| 1912 rpm_urls = [] |
1653 for rpm in rpms: | 1913 for rpm in rpms: |
1654 rpm_name = koji.pathinfo.rpm(rpm) | 1914 rpm_name = koji.pathinfo.rpm(rpm) |
1655 url = ("%s/%s/%s/%s/%s" % (self.koji_options['pkgurl'], | 1915 url = ("%s/%s/%s/%s/%s" % (self.config_options['pkgurl'], |
1656 info['package_name'], | 1916 info['package_name'], |
1657 info['version'], info['release'], | 1917 info['version'], info['release'], |
1658 rpm_name)) | 1918 rpm_name)) |
1659 if rfilter: | 1919 rpm_urls.append(url) |
1660 filter_regexp = re.compile(rfilter, re.IGNORECASE) | 1920 return rpm_urls |
1661 if filter_regexp.match(os.path.basename(rpm_name)): | 1921 |
1662 download = True | 1922 |
1663 else: | 1923 def get_pkgs(self, pkg, dst_dir, arch=None): |
1664 download = False | 1924 ''' |
| 1925 Download the packages |
| 1926 |
| 1927 @type pkg: KojiPkgSpec |
| 1928 @param pkg: a package specification |
| 1929 @type dst_dir: string |
| 1930 @param dst_dir: the destination directory, where the downloaded |
| 1931 packages will be saved on |
| 1932 @type arch: string |
| 1933 @param arch: packages built for this architecture, but also including |
| 1934 architecture independent (noarch) packages |
| 1935 ''' |
| 1936 rpm_urls = self.get_pkg_urls(pkg, arch) |
| 1937 for url in rpm_urls: |
| 1938 utils.get_file(url, |
| 1939 os.path.join(dst_dir, os.path.basename(url))) |
| 1940 |
| 1941 |
| 1942 DEFAULT_KOJI_TAG = None |
| 1943 def set_default_koji_tag(tag): |
| 1944 ''' |
| 1945 Sets the default tag that will be used |
| 1946 ''' |
| 1947 global DEFAULT_KOJI_TAG |
| 1948 DEFAULT_KOJI_TAG = tag |
| 1949 |
| 1950 |
| 1951 def get_default_koji_tag(): |
| 1952 return DEFAULT_KOJI_TAG |
| 1953 |
| 1954 |
| 1955 class KojiPkgSpec: |
| 1956 ''' |
| 1957 A package specification syntax parser for Koji |
| 1958 |
| 1959 This holds information on either tag or build, and packages to be fetched |
| 1960 from koji and possibly installed (features external do this class). |
| 1961 |
| 1962 New objects can be created either by providing information in the textual |
| 1963 format or by using the actual parameters for tag, build, package and sub- |
| 1964 packages. The textual format is useful for command line interfaces and |
| 1965 configuration files, while using parameters is better for using this in |
| 1966 a programatic fashion. |
| 1967 |
| 1968 The following sets of examples are interchangeable. Specifying all packages |
| 1969 part of build number 1000: |
| 1970 |
| 1971 >>> from kvm_utils import KojiPkgSpec |
| 1972 >>> pkg = KojiPkgSpec('1000') |
| 1973 |
| 1974 >>> pkg = KojiPkgSpec(build=1000) |
| 1975 |
| 1976 Specifying only a subset of packages of build number 1000: |
| 1977 |
| 1978 >>> pkg = KojiPkgSpec('1000:kernel,kernel-devel') |
| 1979 |
| 1980 >>> pkg = KojiPkgSpec(build=1000, |
| 1981 subpackages=['kernel', 'kernel-devel']) |
| 1982 |
| 1983 Specifying the latest build for the 'kernel' package tagged with 'dist-f14': |
| 1984 |
| 1985 >>> pkg = KojiPkgSpec('dist-f14:kernel') |
| 1986 |
| 1987 >>> pkg = KojiPkgSpec(tag='dist-f14', package='kernel') |
| 1988 |
| 1989 Specifying the 'kernel' package using the default tag: |
| 1990 |
| 1991 >>> kvm_utils.set_default_koji_tag('dist-f14') |
| 1992 >>> pkg = KojiPkgSpec('kernel') |
| 1993 |
| 1994 >>> pkg = KojiPkgSpec(package='kernel') |
| 1995 |
| 1996 Specifying the 'kernel' package using the default tag: |
| 1997 |
| 1998 >>> kvm_utils.set_default_koji_tag('dist-f14') |
| 1999 >>> pkg = KojiPkgSpec('kernel') |
| 2000 |
| 2001 >>> pkg = KojiPkgSpec(package='kernel') |
| 2002 |
| 2003 If you do not specify a default tag, and give a package name without an |
| 2004 explicit tag, your package specification is considered invalid: |
| 2005 |
| 2006 >>> print kvm_utils.get_default_koji_tag() |
| 2007 None |
| 2008 >>> print kvm_utils.KojiPkgSpec('kernel').is_valid() |
| 2009 False |
| 2010 |
| 2011 >>> print kvm_utils.KojiPkgSpec(package='kernel').is_valid() |
| 2012 False |
| 2013 ''' |
| 2014 |
| 2015 SEP = ':' |
| 2016 |
| 2017 def __init__(self, text='', tag=None, build=None, |
| 2018 package=None, subpackages=[]): |
| 2019 ''' |
| 2020 Instantiates a new KojiPkgSpec object |
| 2021 |
| 2022 @type text: string |
| 2023 @param text: a textual representation of a package on Koji that |
| 2024 will be parsed |
| 2025 @type tag: string |
| 2026 @param tag: a koji tag, example: Fedora-14-RELEASE |
| 2027 (see U{http://fedoraproject.org/wiki/Koji#Tags_and_Targets}) |
| 2028 @type build: number |
| 2029 @param build: a koji build, example: 1001 |
| 2030 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture}) |
| 2031 @type package: string |
| 2032 @param package: a koji package, example: python |
| 2033 (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture}) |
| 2034 @type subpackages: list of strings |
| 2035 @param subpackages: a list of package names, usually a subset of |
| 2036 the RPM packages generated by a given build |
| 2037 ''' |
| 2038 |
| 2039 # Set to None to indicate 'not set' (and be able to use 'is') |
| 2040 self.tag = None |
| 2041 self.build = None |
| 2042 self.package = None |
| 2043 self.subpackages = [] |
| 2044 |
| 2045 self.default_tag = None |
| 2046 |
| 2047 # Textual representation takes precedence (most common use case) |
| 2048 if text: |
| 2049 self.parse(text) |
| 2050 else: |
| 2051 self.tag = tag |
| 2052 self.build = build |
| 2053 self.package = package |
| 2054 self.subpackages = subpackages |
| 2055 |
| 2056 # Set the default tag, if set, as a fallback |
| 2057 if not self.build and not self.tag: |
| 2058 default_tag = get_default_koji_tag() |
| 2059 if default_tag is not None: |
| 2060 self.tag = default_tag |
| 2061 |
| 2062 |
| 2063 def parse(self, text): |
| 2064 ''' |
| 2065 Parses a textual representation of a package specification |
| 2066 |
| 2067 @type text: string |
| 2068 @param text: textual representation of a package in koji |
| 2069 ''' |
| 2070 parts = text.count(self.SEP) + 1 |
| 2071 if parts == 1: |
| 2072 if text.isdigit(): |
| 2073 self.build = text |
1665 else: | 2074 else: |
1666 download = True | 2075 self.package = text |
1667 | 2076 elif parts == 2: |
1668 if download: | 2077 part1, part2 = text.split(self.SEP) |
1669 r = utils.get_file(url, | 2078 if part1.isdigit(): |
1670 os.path.join(dst_dir, os.path.basename(url))) | 2079 self.build = part1 |
1671 rpm_paths.append(r) | 2080 self.subpackages = part2.split(',') |
1672 | 2081 else: |
1673 return rpm_paths | 2082 self.tag = part1 |
| 2083 self.package = part2 |
| 2084 elif parts >= 3: |
| 2085 # Instead of erroring on more arguments, we simply ignore them |
| 2086 # This makes the parser suitable for future syntax additions, such |
| 2087 # as specifying the package architecture |
| 2088 part1, part2, part3 = text.split(self.SEP)[0:3] |
| 2089 self.tag = part1 |
| 2090 self.package = part2 |
| 2091 self.subpackages = part3.split(',') |
| 2092 |
| 2093 |
| 2094 def _is_invalid_neither_tag_or_build(self): |
| 2095 ''' |
| 2096 Checks if this package is invalid due to not having either a valid |
| 2097 tag or build set, that is, both are empty. |
| 2098 |
| 2099 @returns: True if this is invalid and False if it's valid |
| 2100 ''' |
| 2101 return (self.tag is None and self.build is None) |
| 2102 |
| 2103 |
| 2104 def _is_invalid_package_but_no_tag(self): |
| 2105 ''' |
| 2106 Checks if this package is invalid due to having a package name set |
| 2107 but tag or build set, that is, both are empty. |
| 2108 |
| 2109 @returns: True if this is invalid and False if it's valid |
| 2110 ''' |
| 2111 return (self.package and not self.tag) |
| 2112 |
| 2113 |
| 2114 def _is_invalid_subpackages_but_no_main_package(self): |
| 2115 ''' |
| 2116 Checks if this package is invalid due to having a tag set (this is Ok) |
| 2117 but specifying subpackage names without specifying the main package |
| 2118 name. |
| 2119 |
| 2120 Specifying subpackages without a main package name is only valid when |
| 2121 a build is used instead of a tag. |
| 2122 |
| 2123 @returns: True if this is invalid and False if it's valid |
| 2124 ''' |
| 2125 return (self.tag and self.subpackages and not self.package) |
| 2126 |
| 2127 |
| 2128 def is_valid(self): |
| 2129 ''' |
| 2130 Checks if this package specification is valid. |
| 2131 |
| 2132 Being valid means that it has enough and not conflicting information. |
| 2133 It does not validate that the packages specified actually existe on |
| 2134 the Koji server. |
| 2135 |
| 2136 @returns: True or False |
| 2137 ''' |
| 2138 if self._is_invalid_neither_tag_or_build(): |
| 2139 return False |
| 2140 elif self._is_invalid_package_but_no_tag(): |
| 2141 return False |
| 2142 elif self._is_invalid_subpackages_but_no_main_package(): |
| 2143 return False |
| 2144 |
| 2145 return True |
| 2146 |
| 2147 |
| 2148 def describe_invalid(self): |
| 2149 ''' |
| 2150 Describes why this is not valid, in a human friendly way |
| 2151 ''' |
| 2152 if self._is_invalid_neither_tag_or_build(): |
| 2153 return 'neither a tag or build are set, and of them should be set' |
| 2154 elif self._is_invalid_package_but_no_tag(): |
| 2155 return 'package name specified but no tag is set' |
| 2156 elif self._is_invalid_subpackages_but_no_main_package(): |
| 2157 return 'subpackages specified but no main package is set' |
| 2158 |
| 2159 return 'unkwown reason, seems to be valid' |
| 2160 |
| 2161 |
| 2162 def describe(self): |
| 2163 ''' |
| 2164 Describe this package specification, in a human friendly way |
| 2165 |
| 2166 @returns: package specification description |
| 2167 ''' |
| 2168 if self.is_valid(): |
| 2169 description = '' |
| 2170 if not self.subpackages: |
| 2171 description += 'all subpackages from %s ' % self.package |
| 2172 else: |
| 2173 description += ('only subpackage(s) %s from package %s ' % |
| 2174 (', '.join(self.subpackages), self.package)) |
| 2175 |
| 2176 if self.build: |
| 2177 description += 'from build %s' % self.build |
| 2178 elif self.tag: |
| 2179 description += 'tagged with %s' % self.tag |
| 2180 else: |
| 2181 raise ValueError, 'neither build or tag is set' |
| 2182 |
| 2183 return description |
| 2184 else: |
| 2185 return ('Invalid package specification: %s' % |
| 2186 self.describe_invalid()) |
| 2187 |
| 2188 |
| 2189 def __repr__(self): |
| 2190 return ("<KojiPkgSpec tag=%s build=%s pkg=%s subpkgs=%s>" % |
| 2191 (self.tag, self.build, self.package, |
| 2192 ", ".join(self.subpackages))) |
1674 | 2193 |
1675 | 2194 |
1676 def umount(src, mount_point, type): | 2195 def umount(src, mount_point, type): |
1677 """ | 2196 """ |
1678 Umount the src mounted in mount_point. | 2197 Umount the src mounted in mount_point. |
1679 | 2198 |
1680 @src: mount source | 2199 @src: mount source |
1681 @mount_point: mount point | 2200 @mount_point: mount point |
1682 @type: file system type | 2201 @type: file system type |
1683 """ | 2202 """ |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1719 return False | 2238 return False |
1720 | 2239 |
1721 logging.debug("Verify the mount through /etc/mtab") | 2240 logging.debug("Verify the mount through /etc/mtab") |
1722 if mount_string in file("/etc/mtab").read(): | 2241 if mount_string in file("/etc/mtab").read(): |
1723 logging.debug("%s is successfully mounted", src) | 2242 logging.debug("%s is successfully mounted", src) |
1724 return True | 2243 return True |
1725 else: | 2244 else: |
1726 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s", | 2245 logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s", |
1727 file("/etc/mtab").read()) | 2246 file("/etc/mtab").read()) |
1728 return False | 2247 return False |
| 2248 |
| 2249 |
| 2250 def install_host_kernel(job, params): |
| 2251 """ |
| 2252 Install a host kernel, given the appropriate params. |
| 2253 |
| 2254 @param job: Job object. |
| 2255 @param params: Dict with host kernel install params. |
| 2256 """ |
| 2257 install_type = params.get('host_kernel_install_type') |
| 2258 |
| 2259 rpm_url = params.get('host_kernel_rpm_url') |
| 2260 |
| 2261 koji_cmd = params.get('host_kernel_koji_cmd') |
| 2262 koji_build = params.get('host_kernel_koji_build') |
| 2263 koji_tag = params.get('host_kernel_koji_tag') |
| 2264 |
| 2265 git_repo = params.get('host_kernel_git_repo') |
| 2266 git_branch = params.get('host_kernel_git_branch') |
| 2267 git_commit = params.get('host_kernel_git_commit') |
| 2268 patch_list = params.get('host_kernel_patch_list') |
| 2269 if patch_list: |
| 2270 patch_list = patch_list.split() |
| 2271 kernel_config = params.get('host_kernel_config') |
| 2272 |
| 2273 if install_type == 'rpm': |
| 2274 logging.info('Installing host kernel through rpm') |
| 2275 dst = os.path.join("/tmp", os.path.basename(rpm_url)) |
| 2276 k = utils.get_file(rpm_url, dst) |
| 2277 host_kernel = job.kernel(k) |
| 2278 host_kernel.install(install_vmlinux=False) |
| 2279 host_kernel.boot() |
| 2280 |
| 2281 elif install_type in ['koji', 'brew']: |
| 2282 k_deps = KojiPkgSpec(tag=koji_tag, package='kernel', |
| 2283 subpackages=['kernel-devel', 'kernel-firmware']) |
| 2284 k = KojiPkgSpec(tag=koji_tag, package='kernel', |
| 2285 subpackages=['kernel']) |
| 2286 |
| 2287 c = KojiClient(koji_cmd) |
| 2288 logging.info('Fetching kernel dependencies (-devel, -firmware)') |
| 2289 c.get_pkgs(k_deps, job.tmpdir) |
| 2290 logging.info('Installing kernel dependencies (-devel, -firmware) ' |
| 2291 'through %s', install_type) |
| 2292 k_deps_rpm_file_names = [os.path.join(job.tmpdir, rpm_file_name) for |
| 2293 rpm_file_name in c.get_pkg_rpm_file_names(k_dep
s)] |
| 2294 utils.run('rpm -U --force %s' % " ".join(k_deps_rpm_file_names)) |
| 2295 |
| 2296 c.get_pkgs(k, job.tmpdir) |
| 2297 k_rpm = os.path.join(job.tmpdir, |
| 2298 c.get_pkg_rpm_file_names(k)[0]) |
| 2299 host_kernel = job.kernel(k_rpm) |
| 2300 host_kernel.install(install_vmlinux=False) |
| 2301 host_kernel.boot() |
| 2302 |
| 2303 elif install_type == 'git': |
| 2304 logging.info('Chose to install host kernel through git, proceeding') |
| 2305 repodir = os.path.join("/tmp", 'kernel_src') |
| 2306 r = get_git_branch(git_repo, git_branch, repodir, git_commit) |
| 2307 host_kernel = job.kernel(r) |
| 2308 if patch_list: |
| 2309 host_kernel.patch(patch_list) |
| 2310 host_kernel.config(kernel_config) |
| 2311 host_kernel.build() |
| 2312 host_kernel.install() |
| 2313 host_kernel.boot() |
| 2314 |
| 2315 else: |
| 2316 logging.info('Chose %s, using the current kernel for the host', |
| 2317 install_type) |
OLD | NEW |