Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(316)

Side by Side Diff: appengine/swarming/swarming_bot/os_utilities.py

Issue 1308543003: Remove stale android code and remove stand alone functionality. (Closed) Base URL: git@github.com:luci/luci-py.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | appengine/swarming/swarming_bot/os_utilities_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Swarming Authors. All rights reserved. 2 # Copyright 2014 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed by the Apache v2.0 license that can be 3 # Use of this source code is governed by the Apache v2.0 license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """OS specific utility functions. 6 """OS specific utility functions.
7 7
8 Includes code: 8 Includes code:
9 - to declare the current system this code is running under. 9 - to declare the current system this code is running under.
10 - to run a command on user login. 10 - to run a command on user login.
(...skipping 19 matching lines...) Expand all
30 import socket 30 import socket
31 import string 31 import string
32 import subprocess 32 import subprocess
33 import sys 33 import sys
34 import tempfile 34 import tempfile
35 import threading 35 import threading
36 import time 36 import time
37 import urllib 37 import urllib
38 import urllib2 38 import urllib2
39 39
40 try: 40 from utils import file_path
41 # The reason for this try/except is so someone can copy this single file on a 41 from utils import tools
42 # new machine and execute it as-is to get the dimensions that would be set. 42 from utils import zip_package
43 from utils import file_path
44 from utils import tools
45 from utils import zip_package
46 43
47 THIS_FILE = os.path.abspath(zip_package.get_main_script_path() or __file__) 44 THIS_FILE = os.path.abspath(zip_package.get_main_script_path() or __file__)
48 except ImportError as e:
49 THIS_FILE = os.path.abspath(__file__)
50 tools = None
51 print >> sys.stderr, 'Failed to import tools: %s' % e
52
53
54 # Properties from an android device that should be kept as dimension.
55 ANDROID_DETAILS = frozenset(
56 [
57 # Hardware details.
58 u'ro.board.platform',
59 u'ro.product.board', # or ro.product.device?
60 u'ro.product.cpu.abi',
61 u'ro.product.cpu.abi2',
62
63 # OS details.
64 u'ro.build.id',
65 u'ro.build.tags',
66 u'ro.build.type',
67 u'ro.build.version.sdk',
68 ])
69 45
70 46
71 # https://cloud.google.com/compute/pricing#machinetype 47 # https://cloud.google.com/compute/pricing#machinetype
72 GCE_MACHINE_COST_HOUR_US = { 48 GCE_MACHINE_COST_HOUR_US = {
73 u'n1-standard-1': 0.063, 49 u'n1-standard-1': 0.063,
74 u'n1-standard-2': 0.126, 50 u'n1-standard-2': 0.126,
75 u'n1-standard-4': 0.252, 51 u'n1-standard-4': 0.252,
76 u'n1-standard-8': 0.504, 52 u'n1-standard-8': 0.504,
77 u'n1-standard-16': 1.008, 53 u'n1-standard-16': 1.008,
78 u'f1-micro': 0.012, 54 u'f1-micro': 0.012,
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 GCE_SSD_GB_COST_MONTH = 0.17 96 GCE_SSD_GB_COST_MONTH = 0.17
121 97
122 98
123 # https://cloud.google.com/compute/pricing#premiumoperatingsystems 99 # https://cloud.google.com/compute/pricing#premiumoperatingsystems
124 GCE_WINDOWS_COST_CORE_HOUR = 0.04 100 GCE_WINDOWS_COST_CORE_HOUR = 0.04
125 101
126 102
127 ### Private stuff. 103 ### Private stuff.
128 104
129 105
106 # Used to calculated Swarming bot uptime.
130 _STARTED_TS = time.time() 107 _STARTED_TS = time.time()
131 108
109
110 # Cache of GCE OAuth2 token.
132 _CACHED_OAUTH2_TOKEN_GCE = {} 111 _CACHED_OAUTH2_TOKEN_GCE = {}
133 _CACHED_OAUTH2_TOKEN_GCE_LOCK = threading.Lock() 112 _CACHED_OAUTH2_TOKEN_GCE_LOCK = threading.Lock()
134 113
135 _MONITORING_SCOPE = 'https://www.googleapis.com/auth/monitoring'
136
137
138 # Make cached a no-op when client/utils/tools.py is unavailable.
139 if not tools:
140 def cached(func):
141 return func
142 else:
143 cached = tools.cached
144
145 114
146 def _write(filepath, content): 115 def _write(filepath, content):
147 """Writes out a file and returns True on success.""" 116 """Writes out a file and returns True on success."""
148 logging.info('Writing in %s:\n%s', filepath, content) 117 logging.info('Writing in %s:\n%s', filepath, content)
149 try: 118 try:
150 with open(filepath, mode='wb') as f: 119 with open(filepath, mode='wb') as f:
151 f.write(content) 120 f.write(content)
152 return True 121 return True
153 except IOError as e: 122 except IOError as e:
154 logging.error('Failed to write %s: %s', filepath, e) 123 logging.error('Failed to write %s: %s', filepath, e)
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 if dev_type.startswith('03'): 379 if dev_type.startswith('03'):
411 vendor = re_id.match(line[2]) 380 vendor = re_id.match(line[2])
412 device = re_id.match(line[3]) 381 device = re_id.match(line[3])
413 ven_id = vendor.group(2) 382 ven_id = vendor.group(2)
414 dimensions.add(unicode(ven_id)) 383 dimensions.add(unicode(ven_id))
415 dimensions.add(u'%s:%s' % (ven_id, device.group(2))) 384 dimensions.add(u'%s:%s' % (ven_id, device.group(2)))
416 state.add(u'%s %s' % (vendor.group(1), device.group(1))) 385 state.add(u'%s %s' % (vendor.group(1), device.group(1)))
417 return sorted(dimensions), sorted(state) 386 return sorted(dimensions), sorted(state)
418 387
419 388
420 @cached 389 @tools.cached
421 def _get_SPDisplaysDataType_osx(): 390 def _get_SPDisplaysDataType_osx():
422 """Returns an XML about the system display properties.""" 391 """Returns an XML about the system display properties."""
423 import plistlib 392 import plistlib
424 sp = subprocess.check_output( 393 sp = subprocess.check_output(
425 ['system_profiler', 'SPDisplaysDataType', '-xml']) 394 ['system_profiler', 'SPDisplaysDataType', '-xml'])
426 return plistlib.readPlistFromString(sp)[0]['_items'] 395 return plistlib.readPlistFromString(sp)[0]['_items']
427 396
428 397
429 def _get_gpu_osx(): 398 def _get_gpu_osx():
430 """Returns video device as listed by 'system_profiler'. See get_gpu().""" 399 """Returns video device as listed by 'system_profiler'. See get_gpu()."""
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
492 if match: 461 if match:
493 ven_id = match.group(1).lower() 462 ven_id = match.group(1).lower()
494 match = re.search(r'DEV_([0-9A-F]{4})', pnp_string) 463 match = re.search(r'DEV_([0-9A-F]{4})', pnp_string)
495 if match: 464 if match:
496 dev_id = match.group(1).lower() 465 dev_id = match.group(1).lower()
497 dimensions.add(unicode(ven_id)) 466 dimensions.add(unicode(ven_id))
498 dimensions.add(u'%s:%s' % (ven_id, dev_id)) 467 dimensions.add(u'%s:%s' % (ven_id, dev_id))
499 return sorted(dimensions), sorted(state) 468 return sorted(dimensions), sorted(state)
500 469
501 470
502 @cached 471 @tools.cached
503 def _get_mount_points_win(): 472 def _get_mount_points_win():
504 """Returns the list of 'fixed' drives in format 'X:\\'.""" 473 """Returns the list of 'fixed' drives in format 'X:\\'."""
505 ctypes.windll.kernel32.GetDriveTypeW.argtypes = (ctypes.c_wchar_p,) 474 ctypes.windll.kernel32.GetDriveTypeW.argtypes = (ctypes.c_wchar_p,)
506 ctypes.windll.kernel32.GetDriveTypeW.restype = ctypes.c_ulong 475 ctypes.windll.kernel32.GetDriveTypeW.restype = ctypes.c_ulong
507 DRIVE_FIXED = 3 476 DRIVE_FIXED = 3
508 # https://msdn.microsoft.com/library/windows/desktop/aa364939.aspx 477 # https://msdn.microsoft.com/library/windows/desktop/aa364939.aspx
509 return [ 478 return [
510 letter + ':\\' 479 letter + ':\\'
511 for letter in string.lowercase 480 for letter in string.lowercase
512 if ctypes.windll.kernel32.GetDriveTypeW(letter + ':\\') == DRIVE_FIXED 481 if ctypes.windll.kernel32.GetDriveTypeW(letter + ':\\') == DRIVE_FIXED
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 return dict( 523 return dict(
555 ( 524 (
556 items[5], 525 items[5],
557 { 526 {
558 'free_mb': round(float(items[3]) / 1024., 1), 527 'free_mb': round(float(items[3]) / 1024., 1),
559 'size_mb': round(float(items[1]) / 1024., 1), 528 'size_mb': round(float(items[1]) / 1024., 1),
560 } 529 }
561 ) for items in _run_df()) 530 ) for items in _run_df())
562 531
563 532
564 @cached 533 @tools.cached
565 def _get_metadata_gce(): 534 def _get_metadata_gce():
566 """Returns the GCE metadata as a dict. 535 """Returns the GCE metadata as a dict.
567 536
568 Refs: 537 Refs:
569 https://cloud.google.com/compute/docs/metadata 538 https://cloud.google.com/compute/docs/metadata
570 https://cloud.google.com/compute/docs/machine-types 539 https://cloud.google.com/compute/docs/machine-types
571 540
572 To get the at the command line from a GCE VM, use: 541 To get the at the command line from a GCE VM, use:
573 curl --silent \ 542 curl --silent \
574 http://metadata.google.internal/computeMetadata/v1/?recursive=true \ 543 http://metadata.google.internal/computeMetadata/v1/?recursive=true \
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 try: 626 try:
658 with open(filepath, 'rb') as f: 627 with open(filepath, 'rb') as f:
659 return f.read() 628 return f.read()
660 except (IOError, OSError): 629 except (IOError, OSError):
661 return None 630 return None
662 631
663 632
664 ### Public API. 633 ### Public API.
665 634
666 635
667 @cached 636 @tools.cached
668 def get_os_version_number(): 637 def get_os_version_number():
669 """Returns the normalized OS version number as a string. 638 """Returns the normalized OS version number as a string.
670 639
671 Returns: 640 Returns:
672 The format depends on the OS: 641 The format depends on the OS:
673 - Windows: 5.1, 6.1, etc. There is no way to distinguish between Windows 7 642 - Windows: 5.1, 6.1, etc. There is no way to distinguish between Windows 7
674 and Windows Server 2008R2 since they both report 6.1. 643 and Windows Server 2008R2 since they both report 6.1.
675 - OSX: 10.7, 10.8, etc. 644 - OSX: 10.7, 10.8, etc.
676 - Ubuntu: 12.04, 10.04, etc. 645 - Ubuntu: 12.04, 10.04, etc.
677 Others will return None. 646 Others will return None.
(...skipping 19 matching lines...) Expand all
697 666
698 if sys.platform == 'linux2': 667 if sys.platform == 'linux2':
699 # On Ubuntu it will return a string like '12.04'. On Raspbian, it will look 668 # On Ubuntu it will return a string like '12.04'. On Raspbian, it will look
700 # like '7.6'. 669 # like '7.6'.
701 return unicode(platform.linux_distribution()[1]) 670 return unicode(platform.linux_distribution()[1])
702 671
703 logging.error('Unable to determine platform version') 672 logging.error('Unable to determine platform version')
704 return None 673 return None
705 674
706 675
707 @cached 676 @tools.cached
708 def get_os_version_name(): 677 def get_os_version_name():
709 """Returns the marketing name on Windows. 678 """Returns the marketing name on Windows.
710 679
711 Returns None on other OSes, since it's not problematic there. Having 680 Returns None on other OSes, since it's not problematic there. Having
712 dimensions like Trusty or Snow Leopard is not useful. 681 dimensions like Trusty or Snow Leopard is not useful.
713 """ 682 """
714 if sys.platform == 'win32': 683 if sys.platform == 'win32':
715 return _get_os_version_name_win() 684 return _get_os_version_name_win()
716 return None 685 return None
717 686
718 687
719 @cached 688 @tools.cached
720 def get_os_name(): 689 def get_os_name():
721 """Returns standardized OS name. 690 """Returns standardized OS name.
722 691
723 Defaults to sys.platform for OS not normalized. 692 Defaults to sys.platform for OS not normalized.
724 693
725 Returns: 694 Returns:
726 Windows, Mac, Ubuntu, Raspbian, etc. 695 Windows, Mac, Ubuntu, Raspbian, etc.
727 """ 696 """
728 value = { 697 value = {
729 'cygwin': u'Windows', 698 'cygwin': u'Windows',
(...skipping 10 matching lines...) Expand all
740 content = _safe_read('/etc/os-release') 709 content = _safe_read('/etc/os-release')
741 if content: 710 if content:
742 os_release = dict(l.split('=', 1) for l in content.splitlines() if l) 711 os_release = dict(l.split('=', 1) for l in content.splitlines() if l)
743 os_id = os_release.get('ID').strip('"') 712 os_id = os_release.get('ID').strip('"')
744 # Uppercase the first letter for consistency with the other platforms. 713 # Uppercase the first letter for consistency with the other platforms.
745 return unicode(os_id[0].upper() + os_id[1:]) 714 return unicode(os_id[0].upper() + os_id[1:])
746 715
747 return unicode(sys.platform) 716 return unicode(sys.platform)
748 717
749 718
750 @cached 719 @tools.cached
751 def get_cpu_type(): 720 def get_cpu_type():
752 """Returns the type of processor: arm or x86.""" 721 """Returns the type of processor: arm or x86."""
753 machine = platform.machine().lower() 722 machine = platform.machine().lower()
754 if machine in ('amd64', 'x86_64', 'i386'): 723 if machine in ('amd64', 'x86_64', 'i386'):
755 return u'x86' 724 return u'x86'
756 return unicode(machine) 725 return unicode(machine)
757 726
758 727
759 @cached 728 @tools.cached
760 def get_cpu_bitness(): 729 def get_cpu_bitness():
761 """Returns the number of bits in the CPU architecture as a str: 32 or 64. 730 """Returns the number of bits in the CPU architecture as a str: 32 or 64.
762 731
763 Unless someone ported python to PDP-10 or 286. 732 Unless someone ported python to PDP-10 or 286.
764 733
765 Note: this function may return 32 bits on 64 bits OS in case of a 32 bits 734 Note: this function may return 32 bits on 64 bits OS in case of a 32 bits
766 python process. 735 python process.
767 """ 736 """
768 if platform.machine().endswith('64'): 737 if platform.machine().endswith('64'):
769 return u'64' 738 return u'64'
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
805 # happens on OSX. 774 # happens on OSX.
806 hostname = socket.gethostname() 775 hostname = socket.gethostname()
807 return unicode(hostname) 776 return unicode(hostname)
808 777
809 778
810 def get_hostname_short(): 779 def get_hostname_short():
811 """Returns the base host name.""" 780 """Returns the base host name."""
812 return get_hostname().split(u'.', 1)[0] 781 return get_hostname().split(u'.', 1)[0]
813 782
814 783
815 @cached 784 @tools.cached
816 def get_num_processors(): 785 def get_num_processors():
817 """Returns the number of processors. 786 """Returns the number of processors.
818 787
819 Python on OSX 10.6 raises a NotImplementedError exception. 788 Python on OSX 10.6 raises a NotImplementedError exception.
820 """ 789 """
821 try: 790 try:
822 # Multiprocessing 791 # Multiprocessing
823 return multiprocessing.cpu_count() 792 return multiprocessing.cpu_count()
824 except: # pylint: disable=W0702 793 except: # pylint: disable=W0702
825 try: 794 try:
826 # Mac OS 10.6 795 # Mac OS 10.6
827 return int(os.sysconf('SC_NPROCESSORS_ONLN')) # pylint: disable=E1101 796 return int(os.sysconf('SC_NPROCESSORS_ONLN')) # pylint: disable=E1101
828 except: 797 except:
829 # Returns non-zero, otherwise it could generate a divide by zero later 798 # Returns non-zero, otherwise it could generate a divide by zero later
830 # when doing calculations, leading to a crash. Saw it happens on Win2K8R2 799 # when doing calculations, leading to a crash. Saw it happens on Win2K8R2
831 # on python 2.7.5 on cygwin 1.7.28. 800 # on python 2.7.5 on cygwin 1.7.28.
832 logging.error('get_num_processors() failed to query number of cores') 801 logging.error('get_num_processors() failed to query number of cores')
833 # Return an improbable number to make it easier to catch. 802 # Return an improbable number to make it easier to catch.
834 return 5 803 return 5
835 804
836 805
837 @cached 806 @tools.cached
838 def get_physical_ram(): 807 def get_physical_ram():
839 """Returns the amount of installed RAM in Mb, rounded to the nearest number. 808 """Returns the amount of installed RAM in Mb, rounded to the nearest number.
840 """ 809 """
841 if sys.platform == 'win32': 810 if sys.platform == 'win32':
842 # https://msdn.microsoft.com/library/windows/desktop/aa366589.aspx 811 # https://msdn.microsoft.com/library/windows/desktop/aa366589.aspx
843 class MemoryStatusEx(ctypes.Structure): 812 class MemoryStatusEx(ctypes.Structure):
844 _fields_ = [ 813 _fields_ = [
845 ('dwLength', ctypes.c_ulong), 814 ('dwLength', ctypes.c_ulong),
846 ('dwMemoryLoad', ctypes.c_ulong), 815 ('dwMemoryLoad', ctypes.c_ulong),
847 ('dwTotalPhys', ctypes.c_ulonglong), 816 ('dwTotalPhys', ctypes.c_ulonglong),
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
886 return 0 855 return 0
887 856
888 857
889 def get_disks_info(): 858 def get_disks_info():
890 """Returns a dict of dict of free and total disk space.""" 859 """Returns a dict of dict of free and total disk space."""
891 if sys.platform == 'win32': 860 if sys.platform == 'win32':
892 return _get_disks_info_win() 861 return _get_disks_info_win()
893 return _get_disks_info_posix() 862 return _get_disks_info_posix()
894 863
895 864
896 @cached 865 @tools.cached
897 def get_gpu(): 866 def get_gpu():
898 """Returns the installed video card(s) name. 867 """Returns the installed video card(s) name.
899 868
900 Returns: 869 Returns:
901 All the video cards detected. 870 All the video cards detected.
902 tuple(list(dimensions), list(state)). 871 tuple(list(dimensions), list(state)).
903 """ 872 """
904 if sys.platform == 'darwin': 873 if sys.platform == 'darwin':
905 dimensions, state = _get_gpu_osx() 874 dimensions, state = _get_gpu_osx()
906 elif sys.platform == 'linux2': 875 elif sys.platform == 'linux2':
907 dimensions, state = _get_gpu_linux() 876 dimensions, state = _get_gpu_linux()
908 elif sys.platform == 'win32': 877 elif sys.platform == 'win32':
909 dimensions, state = _get_gpu_win() 878 dimensions, state = _get_gpu_win()
910 else: 879 else:
911 dimensions, state = None, None 880 dimensions, state = None, None
912 881
913 # 15ad is VMWare. It's akin not having a GPU card. 882 # 15ad is VMWare. It's akin not having a GPU card.
914 dimensions = dimensions or [u'none'] 883 dimensions = dimensions or [u'none']
915 if '15ad' in dimensions: 884 if '15ad' in dimensions:
916 dimensions.append(u'none') 885 dimensions.append(u'none')
917 dimensions.sort() 886 dimensions.sort()
918 return dimensions, state 887 return dimensions, state
919 888
920 889
921 @cached 890 @tools.cached
922 def get_monitor_hidpi(): 891 def get_monitor_hidpi():
923 """Returns True if there is an hidpi monitor detected.""" 892 """Returns True if there is an hidpi monitor detected."""
924 if sys.platform == 'darwin': 893 if sys.platform == 'darwin':
925 return [_get_monitor_hidpi_osx()] 894 return [_get_monitor_hidpi_osx()]
926 return None 895 return None
927 896
928 897
929 @cached 898 @tools.cached
930 def get_cost_hour(): 899 def get_cost_hour():
931 """Returns the cost in $USD/h as a floating point value if applicable.""" 900 """Returns the cost in $USD/h as a floating point value if applicable."""
932 if _get_metadata_gce(): 901 if _get_metadata_gce():
933 return _get_cost_hour_gce() 902 return _get_cost_hour_gce()
934 903
935 # Get an approximate cost trying to emulate GCE equivalent cost. 904 # Get an approximate cost trying to emulate GCE equivalent cost.
936 cores = get_num_processors() 905 cores = get_num_processors()
937 os_cost = 0. 906 os_cost = 0.
938 if sys.platform == 'darwin': 907 if sys.platform == 'darwin':
939 # Apple tax. It's 50% better, right? 908 # Apple tax. It's 50% better, right?
940 os_cost = GCE_WINDOWS_COST_CORE_HOUR * 1.5 * cores 909 os_cost = GCE_WINDOWS_COST_CORE_HOUR * 1.5 * cores
941 elif sys.platform == 'win32': 910 elif sys.platform == 'win32':
942 # MS tax. 911 # MS tax.
943 os_cost = GCE_WINDOWS_COST_CORE_HOUR * cores 912 os_cost = GCE_WINDOWS_COST_CORE_HOUR * cores
944 913
945 # Guess an equivalent machine_type. 914 # Guess an equivalent machine_type.
946 machine_cost = GCE_MACHINE_COST_HOUR_US.get(get_machine_type(), 0.) 915 machine_cost = GCE_MACHINE_COST_HOUR_US.get(get_machine_type(), 0.)
947 916
948 # Assume HDD for now, it's the cheapest. That's not true, we do have SSDs. 917 # Assume HDD for now, it's the cheapest. That's not true, we do have SSDs.
949 disk_gb_cost = 0. 918 disk_gb_cost = 0.
950 for disk in get_disks_info().itervalues(): 919 for disk in get_disks_info().itervalues():
951 disk_gb_cost += disk['free_mb'] / 1024. * ( 920 disk_gb_cost += disk['free_mb'] / 1024. * (
952 GCE_HDD_GB_COST_MONTH / 30. / 24.) 921 GCE_HDD_GB_COST_MONTH / 30. / 24.)
953 return machine_cost + os_cost + disk_gb_cost 922 return machine_cost + os_cost + disk_gb_cost
954 923
955 924
956 @cached 925 @tools.cached
957 def get_machine_type(): 926 def get_machine_type():
958 """Returns a GCE-equivalent machine type. 927 """Returns a GCE-equivalent machine type.
959 928
960 If running on GCE, returns the right machine type. Otherwise tries to find the 929 If running on GCE, returns the right machine type. Otherwise tries to find the
961 'closest' one. 930 'closest' one.
962 """ 931 """
963 if _get_metadata_gce(): 932 if _get_metadata_gce():
964 return get_machine_type_gce() 933 return get_machine_type_gce()
965 934
966 ram_gb = get_physical_ram() / 1024. 935 ram_gb = get_physical_ram() / 1024.
(...skipping 24 matching lines...) Expand all
991 else: 960 else:
992 machine_type = u'n1-standard-1' 961 machine_type = u'n1-standard-1'
993 else: 962 else:
994 logging.info('Failed to find a fit: %s', machine_type) 963 logging.info('Failed to find a fit: %s', machine_type)
995 964
996 if machine_type not in GCE_MACHINE_COST_HOUR_US: 965 if machine_type not in GCE_MACHINE_COST_HOUR_US:
997 return None 966 return None
998 return machine_type 967 return machine_type
999 968
1000 969
1001 @cached 970 @tools.cached
1002 def can_send_metric(): 971 def can_send_metric():
1003 """True if 'send_metric' really does something.""" 972 """True if 'send_metric' really does something."""
1004 if _get_metadata_gce(): 973 if _get_metadata_gce():
1005 return _MONITORING_SCOPE in _oauth2_available_scopes_gce() 974 # Scope to use Cloud Monitoring.
975 scope = 'https://www.googleapis.com/auth/monitoring'
976 return scope in _oauth2_available_scopes_gce()
1006 return False 977 return False
1007 978
1008 979
1009 def send_metric(name, value): 980 def send_metric(name, value):
1010 if _get_metadata_gce(): 981 if _get_metadata_gce():
1011 return send_metric_gce(name, value) 982 return send_metric_gce(name, value)
1012 # Ignore on other platforms for now. 983 # Ignore on other platforms for now.
1013 984
1014 985
1015 ### Google Cloud Compute Engine. 986 ### Google Cloud Compute Engine.
1016 987
1017 988
1018 @cached 989 @tools.cached
1019 def get_zone_gce(): 990 def get_zone_gce():
1020 """Returns the zone containing the GCE VM.""" 991 """Returns the zone containing the GCE VM."""
1021 metadata = _get_metadata_gce() 992 metadata = _get_metadata_gce()
1022 if not metadata: 993 if not metadata:
1023 return None 994 return None
1024 # Format is projects/<id>/zones/<zone> 995 # Format is projects/<id>/zones/<zone>
1025 return unicode(metadata['instance']['zone'].rsplit('/', 1)[-1]) 996 return unicode(metadata['instance']['zone'].rsplit('/', 1)[-1])
1026 997
1027 998
1028 @cached 999 @tools.cached
1029 def get_machine_type_gce(): 1000 def get_machine_type_gce():
1030 """Returns the GCE machine type.""" 1001 """Returns the GCE machine type."""
1031 metadata = _get_metadata_gce() 1002 metadata = _get_metadata_gce()
1032 if not metadata: 1003 if not metadata:
1033 return None 1004 return None
1034 # Format is projects/<id>/machineTypes/<machine_type> 1005 # Format is projects/<id>/machineTypes/<machine_type>
1035 return unicode(metadata['instance']['machineType'].rsplit('/', 1)[-1]) 1006 return unicode(metadata['instance']['machineType'].rsplit('/', 1)[-1])
1036 1007
1037 1008
1038 @cached 1009 @tools.cached
1039 def get_tags_gce(): 1010 def get_tags_gce():
1040 """Returns a list of instance tags or empty list if not GCE VM.""" 1011 """Returns a list of instance tags or empty list if not GCE VM."""
1041 metadata = _get_metadata_gce() 1012 metadata = _get_metadata_gce()
1042 if not metadata: 1013 if not metadata:
1043 return [] 1014 return []
1044 return metadata['instance']['tags'] 1015 return metadata['instance']['tags']
1045 1016
1046 1017
1047 def send_metric_gce(name, value): 1018 def send_metric_gce(name, value):
1048 """Sets a lightweight custom metric. 1019 """Sets a lightweight custom metric.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1104 logging.debug(json.load(resp)) 1075 logging.debug(json.load(resp))
1105 except urllib2.HTTPError as e: 1076 except urllib2.HTTPError as e:
1106 logging.error('send_metric failed: %s: %s' % (e, e.read())) 1077 logging.error('send_metric failed: %s: %s' % (e, e.read()))
1107 except IOError as e: 1078 except IOError as e:
1108 logging.error('send_metric failed: %s' % e) 1079 logging.error('send_metric failed: %s' % e)
1109 1080
1110 1081
1111 ### Windows. 1082 ### Windows.
1112 1083
1113 1084
1114 @cached 1085 @tools.cached
1115 def get_integrity_level_win(): 1086 def get_integrity_level_win():
1116 """Returns the integrity level of the current process as a string. 1087 """Returns the integrity level of the current process as a string.
1117 1088
1118 TODO(maruel): It'd be nice to make it work on cygwin. The problem is that 1089 TODO(maruel): It'd be nice to make it work on cygwin. The problem is that
1119 ctypes.windll is unaccessible and it is not known to the author how to use 1090 ctypes.windll is unaccessible and it is not known to the author how to use
1120 stdcall convention through ctypes.cdll. 1091 stdcall convention through ctypes.cdll.
1121 """ 1092 """
1122 if sys.platform != 'win32': 1093 if sys.platform != 'win32':
1123 return None 1094 return None
1124 if get_os_version_number() == u'5.1': 1095 if get_os_version_number() == u'5.1':
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
1228 token_info.Label.Sid, p_sid_size.contents.value - 1) 1199 token_info.Label.Sid, p_sid_size.contents.value - 1)
1229 value = res.contents.value 1200 value = res.contents.value
1230 return mapping.get(value) or u'0x%04x' % value 1201 return mapping.get(value) or u'0x%04x' % value
1231 finally: 1202 finally:
1232 ctypes.windll.kernel32.CloseHandle(token) 1203 ctypes.windll.kernel32.CloseHandle(token)
1233 1204
1234 1205
1235 ### Android. 1206 ### Android.
1236 1207
1237 1208
1238 def get_adb_list_devices(adb_path='adb'):
1239 """Returns the list of devices available. This includes emulators."""
1240 output = subprocess.check_output([adb_path, 'devices'])
1241 devices = []
1242 for line in output.splitlines():
1243 if line.startswith(('*', 'List of')) or not line:
1244 continue
1245 # TODO(maruel): Handle 'offline', 'device', 'no device' and
1246 # 'unauthorized'.
1247 devices.append(line.split()[0])
1248 return devices
1249
1250
1251 def get_adb_device_properties_raw(device_id, adb_path='adb'):
1252 """Returns the system properties for a device."""
1253 output = subprocess.check_output(
1254 [adb_path, '-s', device_id, 'shell', 'cat', '/system/build.prop'])
1255 properties = {}
1256 for line in output.splitlines():
1257 if line.startswith('#') or not line:
1258 continue
1259 key, value = line.split('=', 1)
1260 properties[key] = value
1261 return properties
1262
1263
1264 def get_dimensions_android(device_id, adb_path='adb'):
1265 """Returns the default dimensions for an android device.
1266
1267 In this case, details are about the device, not about the host.
1268 """
1269 properties = get_adb_device_properties_raw(device_id, adb_path)
1270 out = dict(
1271 (k, [v]) for k, v in properties.iteritems() if k in ANDROID_DETAILS)
1272 out[u'id'] = [device_id]
1273 return out
1274
1275
1276 def get_state_android(device_id, adb_path='adb'):
1277 """Returns state information about the device.
1278
1279 It's a big speculating TODO. Would be temperature, device uptime, partition
1280 space, etc.
1281 """
1282 # Unused argument - pylint: disable=W0613
1283 return {
1284 u'device': {
1285 # TODO(maruel): Fill me.
1286 },
1287 u'host': get_state(),
1288 }
1289 1209
1290 1210
1291 ### 1211 ###
1292 1212
1293 1213
1294 def get_dimensions(): 1214 def get_dimensions():
1295 """Returns the default dimensions.""" 1215 """Returns the default dimensions."""
1296 os_name = get_os_name() 1216 os_name = get_os_name()
1297 cpu_type = get_cpu_type() 1217 cpu_type = get_cpu_type()
1298 cpu_bitness = get_cpu_bitness() 1218 cpu_bitness = get_cpu_bitness()
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after
1620 u'dimensions': get_dimensions(), 1540 u'dimensions': get_dimensions(),
1621 u'state': get_state(), 1541 u'state': get_state(),
1622 } 1542 }
1623 json.dump(data, sys.stdout, indent=2, sort_keys=True, separators=(',', ': ')) 1543 json.dump(data, sys.stdout, indent=2, sort_keys=True, separators=(',', ': '))
1624 print('') 1544 print('')
1625 return 0 1545 return 0
1626 1546
1627 1547
1628 if __name__ == '__main__': 1548 if __name__ == '__main__':
1629 sys.exit(main()) 1549 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | appengine/swarming/swarming_bot/os_utilities_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698