Keine Beschreibung

utils.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import json
  2. import os
  3. import re
  4. from datetime import datetime
  5. from pytz import utc
  6. from linode.api import Api
  7. _datacenter_dict = None
  8. _pricing_plan_dict = None
  9. _distribution_dict = None
  10. _stackscript_dict = None
  11. _linode_dict = None
  12. _kernel_dict = None
  13. def get_api(apiKey):
  14. return Api(key=apiKey)
  15. def get_datacenter_dict(api, reload=False):
  16. global _datacenter_dict
  17. if reload or (_datacenter_dict is None):
  18. _datacenter_dict = {dc['ABBR']: dc for dc in api.avail_datacenters()}
  19. return _datacenter_dict
  20. def get_pricing_plan_dict(api, reload=False):
  21. global _pricing_plan_dict
  22. if reload or (_pricing_plan_dict is None):
  23. _pricing_plan_dict = {plan['LABEL']: plan for plan in api.avail_linodeplans()}
  24. return _pricing_plan_dict
  25. def get_distribution_dict(api, reload=False):
  26. global _distribution_dict
  27. if reload or (_distribution_dict is None):
  28. _distribution_dict = {dist['LABEL']: dist for dist in api.avail_distributions()}
  29. return _distribution_dict
  30. def get_stackscript_dict(api, reload=False):
  31. global _stackscript_dict
  32. if reload or (_stackscript_dict is None):
  33. _stackscript_dict = {script['LABEL']: script for script in api.stackscript_list()}
  34. return _stackscript_dict
  35. def get_kernel_dict(api, reload=False):
  36. global _kernel_dict
  37. if reload or (_kernel_dict is None):
  38. _kernel_dict = {kernel['LABEL']: kernel for kernel in api.avail_kernels()}
  39. return _kernel_dict
  40. def get_latest_kernel(api, is_64_bit=True):
  41. kernels = get_kernel_dict(api)
  42. prefix = 'latest 64 bit' if is_64_bit else 'latest 32 bit'
  43. for label, kernel in kernels.items():
  44. if label.lower().startswith(prefix):
  45. return kernel
  46. raise RuntimeError('Latest %s bit kernel not found' % ('64' if is_64_bit else '32',))
  47. def get_linode_dict(api, reload=False):
  48. global _linode_dict
  49. if reload or (_linode_dict is None):
  50. _linode_dict = {linode['LABEL']: linode for linode in api.linode_list()}
  51. return _linode_dict
  52. def get_linode_by_label(api, linode_label, reload=False):
  53. linodes = get_linode_dict(api, reload)
  54. if linode_label in linodes:
  55. return linodes[linode_label]
  56. else:
  57. raise ValueError('Invalid Linode label: ' + linode_label)
  58. def get_linode_by_id(api, linode_id, reload=False):
  59. linodes = get_linode_dict(api, reload)
  60. for linode in linodes.values():
  61. if linode['LINODEID'] == linode_id:
  62. return linode
  63. raise ValueError('Invalid Linode ID: ' + linode_id)
  64. def get_swap_disk(api, linode_id):
  65. for disk in api.linode_disk_list(LinodeID=linode_id):
  66. if disk['TYPE'] == 'swap':
  67. return disk
  68. return None
  69. def get_data_disk(api, linode_id):
  70. for disk in api.linode_disk_list(LinodeID=linode_id):
  71. if disk['LABEL'].lower() == 'data':
  72. return disk
  73. return None
  74. def create_data_disk(api, linode_id, data_disk_size_mb):
  75. disk = api.linode_disk_create(LinodeID=linode_id, Size=data_disk_size_mb, Label="Data", Type="ext4")
  76. return disk['DiskID']
  77. def create_swap_disk(api, linode_id, swap_disk_size_mb):
  78. disk = api.linode_disk_create(LinodeID=linode_id, Size=swap_disk_size_mb, Label="Swap", Type="swap")
  79. return disk['DiskID']
  80. def create_linode(api, datacenter_abbr, pricing_plan_label):
  81. dcs = get_datacenter_dict(api)
  82. if datacenter_abbr not in dcs:
  83. raise ValueError('Invalid datacenter: ' + datacenter_abbr)
  84. plans = get_pricing_plan_dict(api)
  85. if pricing_plan_label not in plans:
  86. raise ValueError('Invalid pricing plan: ' + pricing_plan_label)
  87. dc_id = dcs[datacenter_abbr]['DATACENTERID']
  88. plan_id = plans[pricing_plan_label]['PLANID']
  89. return api.linode_create(DatacenterID=dc_id, PlanID=plan_id)
  90. def deploy_linode_from_stackscript(api, linode_id, configuration_label, stackscript_label, stackscript_args, distribution_label, disk_label, disk_size_mb, root_password, kernel_id=None, extra_disk_ids=[]):
  91. scripts = get_stackscript_dict(api)
  92. script = scripts.get(stackscript_label)
  93. if script is None:
  94. raise ValueError('Invalid stackscript: ' + stackscript_label)
  95. dists = get_distribution_dict(api)
  96. dist = dists.get(distribution_label)
  97. if dist is None:
  98. raise ValueError('Invalid distribution: ' + distribution_label)
  99. if not str(dist['DISTRIBUTIONID']) in str(script['DISTRIBUTIONIDLIST']).split(','):
  100. raise ValueError('The specified StackScript does not support the specified distribution')
  101. if kernel_id is None:
  102. kernel = get_latest_kernel(api, dist['IS64BIT'] != 0)
  103. kernel_id = kernel['KERNELID']
  104. disk = api.linode_disk_createfromstackscript(
  105. LinodeID=linode_id,
  106. StackScriptID=script['STACKSCRIPTID'],
  107. StackScriptUDFResponses=json.dumps(stackscript_args),
  108. DistributionID=dist['DISTRIBUTIONID'],
  109. Label=disk_label,
  110. Size=disk_size_mb,
  111. rootPass=root_password
  112. )
  113. disk_list = ','.join(str(x) for x in [disk['DiskID']] + list(extra_disk_ids))
  114. config = api.linode_config_create(
  115. LinodeID=linode_id,
  116. KernelID=kernel_id,
  117. Label=configuration_label,
  118. DiskList=disk_list,
  119. helper_distro=True,
  120. helper_disableUpdateDB=True,
  121. helper_depmod=True,
  122. devtmpfs_automount=True,
  123. helper_network=True
  124. )
  125. return config
  126. def parse_asn1_utctime(value):
  127. if value == '':
  128. return None
  129. else:
  130. matches = re.match(r'^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z$', value)
  131. if matches:
  132. year = int(matches.group(1))
  133. year = year + (2000 if year <= 50 else 1900)
  134. return datetime(year, int(matches.group(2)), int(matches.group(3)), int(matches.group(4)), int(matches.group(5)), int(matches.group(6)), tzinfo=utc)
  135. else:
  136. raise ValueError("Invalid ASN1 UTCTime value: {}".format(value))
  137. def load_openssl_ca_index(ca_root_dir):
  138. ret = {}
  139. with open(os.path.join(ca_root_dir, 'index.txt'), 'r') as f:
  140. # Later instances of the same certificate name will replace earlier
  141. # ones.
  142. for line in f:
  143. fields = line.rstrip("\r\n").split("\t")
  144. # As per OpenSSL's apps/apps.h
  145. ret[fields[5]] = {
  146. 'type': fields[0],
  147. 'exp_date': parse_asn1_utctime(fields[1]),
  148. 'rev_date': parse_asn1_utctime(fields[2]),
  149. 'serial': fields[3],
  150. 'file': fields[4],
  151. 'name': fields[5],
  152. }
  153. return ret
  154. def find_cert_with_common_name(ca_index, common_name):
  155. suffix = '/CN=' + common_name
  156. for key, value in ca_index.items():
  157. if key.endswith(suffix):
  158. return value
  159. return None