Manage a pool of nodes for a distributed test infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. # Copyright 2018 Red Hat
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  12. # implied.
  13. #
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import math
  17. import voluptuous as v
  18. from nodepool.driver import ConfigPool
  19. from nodepool.driver import ConfigValue
  20. from nodepool.driver import ProviderConfig
  21. class ProviderCloudImage(ConfigValue):
  22. def __init__(self):
  23. self.name = None
  24. self.image_id = None
  25. self.image_name = None
  26. self.username = None
  27. self.connection_type = None
  28. self.connection_port = None
  29. def __eq__(self, other):
  30. if isinstance(other, ProviderCloudImage):
  31. return (self.name == other.name
  32. and self.image_id == other.image_id
  33. and self.image_name == other.image_name
  34. and self.username == other.username
  35. and self.connection_type == other.connection_type
  36. and self.connection_port == other.connection_port)
  37. return False
  38. def __repr__(self):
  39. return "<ProviderCloudImage %s>" % self.name
  40. @property
  41. def external_name(self):
  42. '''Human readable version of external.'''
  43. return self.image_id or self.image_name or self.name
  44. class ProviderLabel(ConfigValue):
  45. def __init__(self):
  46. self.name = None
  47. self.cloud_image = None
  48. self.flavor_name = None
  49. self.key_name = None
  50. self.volume_size = None
  51. self.volume_type = None
  52. # The ProviderPool object that owns this label.
  53. self.pool = None
  54. def __eq__(self, other):
  55. if isinstance(other, ProviderLabel):
  56. # NOTE(Shrews): We intentionally do not compare 'pool' here
  57. # since this causes recursive checks with ProviderPool.
  58. return (other.name == self.name
  59. and other.cloud_image == self.cloud_image
  60. and other.flavor_name == self.flavor_name
  61. and other.key_name == self.key_name
  62. and other.volume_size == self.volume_size
  63. and other.volume_type == self.volume_type)
  64. return False
  65. def __repr__(self):
  66. return "<ProviderLabel %s>" % self.name
  67. class ProviderPool(ConfigPool):
  68. def __init__(self):
  69. self.name = None
  70. self.max_cores = None
  71. self.max_ram = None
  72. self.ignore_provider_quota = False
  73. self.availability_zone = None
  74. self.subnet_id = None
  75. self.security_group_id = None
  76. self.host_key_checking = True
  77. self.labels = None
  78. # The ProviderConfig object that owns this pool.
  79. self.provider = None
  80. # Initialize base class attributes
  81. super().__init__()
  82. def load(self, pool_config, full_config, provider):
  83. super().load(pool_config)
  84. self.name = pool_config['name']
  85. self.provider = provider
  86. self.max_cores = pool_config.get('max-cores', math.inf)
  87. self.max_ram = pool_config.get('max-ram', math.inf)
  88. self.ignore_provider_quota = pool_config.get(
  89. 'ignore-provider-quota', False)
  90. self.availability_zone = pool_config.get('availability-zone')
  91. self.security_group_id = pool_config.get('security-group-id')
  92. self.subnet_id = pool_config.get('subnet-id')
  93. self.host_key_checking = bool(
  94. pool_config.get('host-key-checking', True))
  95. for label in pool_config.get('labels', []):
  96. pl = ProviderLabel()
  97. pl.name = label['name']
  98. pl.pool = self
  99. self.labels[pl.name] = pl
  100. cloud_image_name = label.get('cloud-image', None)
  101. if cloud_image_name:
  102. cloud_image = self.provider.cloud_images.get(
  103. cloud_image_name, None)
  104. if not cloud_image:
  105. raise ValueError(
  106. "cloud-image %s does not exist in provider %s"
  107. " but is referenced in label %s" %
  108. (cloud_image_name, self.name, pl.name))
  109. else:
  110. cloud_image = None
  111. pl.cloud_image = cloud_image
  112. pl.flavor_name = label['flavor-name']
  113. pl.key_name = label['key-name']
  114. pl.volume_type = label.get('volume-type')
  115. pl.volume_size = label.get('volume-size')
  116. full_config.labels[label['name']].pools.append(self)
  117. def __eq__(self, other):
  118. if isinstance(other, ProviderPool):
  119. # NOTE(Shrews): We intentionally do not compare 'provider' here
  120. # since this causes recursive checks with OpenStackProviderConfig.
  121. return (super().__eq__(other)
  122. and other.name == self.name
  123. and other.max_cores == self.max_cores
  124. and other.max_ram == self.max_ram
  125. and other.ignore_provider_quota == (
  126. self.ignore_provider_quota)
  127. and other.availability_zone == self.availability_zone
  128. and other.subnet_id == self.subnet_id
  129. and other.security_group_id == self.security_group_id
  130. and other.host_key_checking == self.host_key_checking
  131. and other.labels == self.labels)
  132. return False
  133. def __repr__(self):
  134. return "<ProviderPool %s>" % self.name
  135. class AwsProviderConfig(ProviderConfig):
  136. def __init__(self, driver, provider):
  137. self.driver_object = driver
  138. self.__pools = {}
  139. self.profile_name = None
  140. self.region_name = None
  141. self.rate = None
  142. self.boot_timeout = None
  143. self.launch_retries = None
  144. self.launch_timeout = None
  145. self.cloud_images = {}
  146. self.hostname_format = None
  147. self.image_name_format = None
  148. super().__init__(provider)
  149. def __eq__(self, other):
  150. if isinstance(other, AwsProviderConfig):
  151. return (super().__eq__(other)
  152. and other.profile_name == self.profile_name
  153. and other.region_name == self.region_name
  154. and other.pools == self.pools
  155. and other.rate == self.rate
  156. and other.boot_timeout == self.boot_timeout
  157. and other.launch_retries == self.launch_retries
  158. and other.launch_timeout == self.launch_timeout
  159. and other.cloud_images == self.cloud_images)
  160. return False
  161. @property
  162. def pools(self):
  163. return self.__pools
  164. @property
  165. def manage_images(self):
  166. return True
  167. @staticmethod
  168. def reset():
  169. pass
  170. def load(self, config):
  171. self.profile_name = self.provider.get('profile-name')
  172. self.region_name = self.provider.get('region-name')
  173. self.rate = float(self.provider.get('rate', 1.0))
  174. self.boot_timeout = self.provider.get('boot-timeout', 60)
  175. self.launch_retries = self.provider.get('launch-retries', 3)
  176. self.launch_timeout = self.provider.get('launch-timeout', 3600)
  177. self.hostname_format = self.provider.get(
  178. 'hostname-format',
  179. '{label.name}-{provider.name}-{node.id}'
  180. )
  181. self.image_name_format = self.provider.get(
  182. 'image-name-format',
  183. '{image_name}-{timestamp}'
  184. )
  185. default_port_mapping = {
  186. 'ssh': 22,
  187. 'winrm': 5986,
  188. }
  189. # TODO: diskimages
  190. for image in self.provider.get('cloud-images', []):
  191. i = ProviderCloudImage()
  192. i.name = image['name']
  193. i.image_id = image.get('image-id', None)
  194. i.image_name = image.get('image-name', None)
  195. i.username = image.get('username', None)
  196. i.connection_type = image.get('connection-type', 'ssh')
  197. i.connection_port = image.get(
  198. 'connection-port',
  199. default_port_mapping.get(i.connection_type, 22))
  200. self.cloud_images[i.name] = i
  201. for pool in self.provider.get('pools', []):
  202. pp = ProviderPool()
  203. pp.load(pool, config, self)
  204. self.pools[pp.name] = pp
  205. def getSchema(self):
  206. pool_label = {
  207. v.Required('name'): str,
  208. v.Exclusive('cloud-image', 'label-image'): str,
  209. v.Required('flavor-name'): str,
  210. v.Required('key-name'): str,
  211. 'volume-type': str,
  212. 'volume-size': int
  213. }
  214. pool = ConfigPool.getCommonSchemaDict()
  215. pool.update({
  216. v.Required('name'): str,
  217. v.Required('labels'): [pool_label],
  218. 'max-cores': int,
  219. 'max-ram': int,
  220. 'availability-zone': str,
  221. 'security-group-id': str,
  222. 'subnet-id': str,
  223. })
  224. provider_cloud_images = {
  225. 'name': str,
  226. 'connection-type': str,
  227. 'connection-port': int,
  228. v.Exclusive('image-id', 'cloud-image-name-or-id'): str,
  229. v.Exclusive('image-name', 'cloud-image-name-or-id'): str,
  230. 'username': str,
  231. }
  232. provider = ProviderConfig.getCommonSchemaDict()
  233. provider.update({
  234. v.Required('pools'): [pool],
  235. v.Required('region-name'): str,
  236. 'profile-name': str,
  237. 'cloud-images': [provider_cloud_images],
  238. 'rate': v.Coerce(float),
  239. 'hostname-format': str,
  240. 'image-name-format': str,
  241. 'boot-timeout': int,
  242. 'launch-timeout': int,
  243. 'launch-retries': int,
  244. })
  245. return v.Schema(provider)
  246. def getSupportedLabels(self, pool_name=None):
  247. labels = set()
  248. for pool in self.pools.values():
  249. if not pool_name or (pool.name == pool_name):
  250. labels.update(pool.labels.keys())
  251. return labels