services.py 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. """
  2. Services implemented by the OpenStack provider.
  3. """
  4. import logging
  5. from neutronclient.common.exceptions import NeutronClientException
  6. from neutronclient.common.exceptions import PortNotFoundClient
  7. from novaclient.exceptions import NotFound as NovaNotFound
  8. from openstack.exceptions import BadRequestException
  9. from openstack.exceptions import HttpException
  10. from openstack.exceptions import NotFoundException
  11. from openstack.exceptions import ResourceNotFound
  12. from swiftclient import ClientException as SwiftClientException
  13. import cloudbridge.base.helpers as cb_helpers
  14. from cloudbridge.base.middleware import dispatch
  15. from cloudbridge.base.resources import BaseLaunchConfig
  16. from cloudbridge.base.resources import ClientPagedResultList
  17. from cloudbridge.base.services import BaseBucketObjectService
  18. from cloudbridge.base.services import BaseBucketService
  19. from cloudbridge.base.services import BaseComputeService
  20. from cloudbridge.base.services import BaseDnsRecordService
  21. from cloudbridge.base.services import BaseDnsService
  22. from cloudbridge.base.services import BaseDnsZoneService
  23. from cloudbridge.base.services import BaseFloatingIPService
  24. from cloudbridge.base.services import BaseGatewayService
  25. from cloudbridge.base.services import BaseImageService
  26. from cloudbridge.base.services import BaseInstanceService
  27. from cloudbridge.base.services import BaseKeyPairService
  28. from cloudbridge.base.services import BaseNetworkService
  29. from cloudbridge.base.services import BaseNetworkingService
  30. from cloudbridge.base.services import BaseRegionService
  31. from cloudbridge.base.services import BaseRouterService
  32. from cloudbridge.base.services import BaseSecurityService
  33. from cloudbridge.base.services import BaseSnapshotService
  34. from cloudbridge.base.services import BaseStorageService
  35. from cloudbridge.base.services import BaseSubnetService
  36. from cloudbridge.base.services import BaseVMFirewallRuleService
  37. from cloudbridge.base.services import BaseVMFirewallService
  38. from cloudbridge.base.services import BaseVMTypeService
  39. from cloudbridge.base.services import BaseVolumeService
  40. from cloudbridge.interfaces.exceptions \
  41. import CloudBridgeBaseException
  42. from cloudbridge.interfaces.exceptions \
  43. import DuplicateResourceException
  44. from cloudbridge.interfaces.exceptions import InvalidParamException
  45. from cloudbridge.interfaces.exceptions import InvalidValueException
  46. from cloudbridge.interfaces.resources import KeyPair
  47. from cloudbridge.interfaces.resources import MachineImage
  48. from cloudbridge.interfaces.resources import Network
  49. from cloudbridge.interfaces.resources import Snapshot
  50. from cloudbridge.interfaces.resources import Subnet
  51. from cloudbridge.interfaces.resources import TrafficDirection
  52. from cloudbridge.interfaces.resources import VMFirewall
  53. from cloudbridge.interfaces.resources import VMType
  54. from cloudbridge.interfaces.resources import Volume
  55. from . import helpers as oshelpers
  56. from .resources import OpenStackBucket
  57. from .resources import OpenStackBucketObject
  58. from .resources import OpenStackDnsRecord
  59. from .resources import OpenStackDnsZone
  60. from .resources import OpenStackFloatingIP
  61. from .resources import OpenStackInstance
  62. from .resources import OpenStackInternetGateway
  63. from .resources import OpenStackKeyPair
  64. from .resources import OpenStackMachineImage
  65. from .resources import OpenStackNetwork
  66. from .resources import OpenStackRegion
  67. from .resources import OpenStackRouter
  68. from .resources import OpenStackSnapshot
  69. from .resources import OpenStackSubnet
  70. from .resources import OpenStackVMFirewall
  71. from .resources import OpenStackVMFirewallRule
  72. from .resources import OpenStackVMType
  73. from .resources import OpenStackVolume
  74. log = logging.getLogger(__name__)
  75. class OpenStackSecurityService(BaseSecurityService):
  76. def __init__(self, provider):
  77. super(OpenStackSecurityService, self).__init__(provider)
  78. # pylint:disable=protected-access
  79. self.service_zone_name = self.provider._get_config_value(
  80. 'os_security_zone_name', cb_helpers.get_env(
  81. 'OS_SECURITY_ZONE_NAME', self.provider.zone_name))
  82. # Initialize provider services
  83. self._key_pairs = OpenStackKeyPairService(provider)
  84. self._vm_firewalls = OpenStackVMFirewallService(provider)
  85. self._vm_firewall_rule_svc = OpenStackVMFirewallRuleService(provider)
  86. @property
  87. def key_pairs(self):
  88. return self._key_pairs
  89. @property
  90. def vm_firewalls(self):
  91. return self._vm_firewalls
  92. @property
  93. def _vm_firewall_rules(self):
  94. return self._vm_firewall_rule_svc
  95. def get_or_create_ec2_credentials(self):
  96. """
  97. A provider specific method than returns the ec2 credentials for the
  98. current user, or creates a new pair if one doesn't exist.
  99. """
  100. keystone = self.provider.keystone
  101. if hasattr(keystone, 'ec2'):
  102. user_id = keystone.session.get_user_id()
  103. user_creds = [cred for cred in keystone.ec2.list(user_id) if
  104. cred.tenant_id == keystone.session.get_project_id()]
  105. if user_creds:
  106. return user_creds[0]
  107. else:
  108. return keystone.ec2.create(
  109. user_id, keystone.session.get_project_id())
  110. return None
  111. def get_ec2_endpoints(self):
  112. """
  113. A provider specific method than returns the ec2 endpoints if
  114. available.
  115. """
  116. keystone = self.provider.keystone
  117. ec2_url = keystone.session.get_endpoint(service_type='ec2')
  118. s3_url = keystone.session.get_endpoint(service_type='s3')
  119. return {'ec2_endpoint': ec2_url,
  120. 's3_endpoint': s3_url}
  121. class OpenStackKeyPairService(BaseKeyPairService):
  122. def __init__(self, provider):
  123. super(OpenStackKeyPairService, self).__init__(provider)
  124. @dispatch(event="provider.security.key_pairs.get",
  125. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  126. def get(self, key_pair_id):
  127. """
  128. Returns a KeyPair given its id.
  129. """
  130. log.debug("Returning KeyPair with the id %s", key_pair_id)
  131. try:
  132. return OpenStackKeyPair(
  133. self.provider, self.provider.nova.keypairs.get(key_pair_id))
  134. except NovaNotFound:
  135. log.debug("KeyPair %s was not found.", key_pair_id)
  136. return None
  137. @dispatch(event="provider.security.key_pairs.list",
  138. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  139. def list(self, limit=None, marker=None):
  140. """
  141. List all key pairs associated with this account.
  142. :rtype: ``list`` of :class:`.KeyPair`
  143. :return: list of KeyPair objects
  144. """
  145. keypairs = self.provider.nova.keypairs.list()
  146. results = [OpenStackKeyPair(self.provider, kp)
  147. for kp in keypairs]
  148. log.debug("Listing all key pairs associated with OpenStack "
  149. "Account: %s", results)
  150. return ClientPagedResultList(self.provider, results,
  151. limit=limit, marker=marker)
  152. @dispatch(event="provider.security.key_pairs.find",
  153. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  154. def find(self, **kwargs):
  155. name = kwargs.pop('name', None)
  156. # All kwargs should have been popped at this time.
  157. if len(kwargs) > 0:
  158. raise InvalidParamException(
  159. "Unrecognised parameters for search: %s. Supported "
  160. "attributes: %s" % (kwargs, 'name'))
  161. keypairs = self.provider.nova.keypairs.findall(name=name)
  162. results = [OpenStackKeyPair(self.provider, kp)
  163. for kp in keypairs]
  164. log.debug("Searching for %s in: %s", name, keypairs)
  165. return ClientPagedResultList(self.provider, results)
  166. @dispatch(event="provider.security.key_pairs.create",
  167. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  168. def create(self, name, public_key_material=None):
  169. OpenStackKeyPair.assert_valid_resource_name(name)
  170. existing_kp = self.find(name=name)
  171. if existing_kp:
  172. raise DuplicateResourceException(
  173. 'Keypair already exists with name {0}'.format(name))
  174. private_key = None
  175. if not public_key_material:
  176. public_key_material, private_key = cb_helpers.generate_key_pair()
  177. kp = self.provider.nova.keypairs.create(name,
  178. public_key=public_key_material)
  179. cb_kp = OpenStackKeyPair(self.provider, kp)
  180. cb_kp.material = private_key
  181. return cb_kp
  182. @dispatch(event="provider.security.key_pairs.delete",
  183. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  184. def delete(self, key_pair):
  185. keypair = (key_pair if isinstance(key_pair, OpenStackKeyPair)
  186. else self.get(key_pair))
  187. if keypair:
  188. # pylint:disable=protected-access
  189. keypair._key_pair.delete()
  190. class OpenStackVMFirewallService(BaseVMFirewallService):
  191. def __init__(self, provider):
  192. super(OpenStackVMFirewallService, self).__init__(provider)
  193. @dispatch(event="provider.security.vm_firewalls.get",
  194. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  195. def get(self, vm_firewall_id):
  196. try:
  197. return OpenStackVMFirewall(
  198. self.provider,
  199. self.provider.os_conn.network
  200. .get_security_group(vm_firewall_id))
  201. except (ResourceNotFound, NotFoundException):
  202. log.debug("Firewall %s not found.", vm_firewall_id)
  203. return None
  204. @dispatch(event="provider.security.vm_firewalls.list",
  205. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  206. def list(self, limit=None, marker=None):
  207. firewalls = [
  208. OpenStackVMFirewall(self.provider, fw)
  209. for fw in self.provider.os_conn.network.security_groups()]
  210. return ClientPagedResultList(self.provider, firewalls,
  211. limit=limit, marker=marker)
  212. @cb_helpers.deprecated_alias(network_id='network')
  213. @dispatch(event="provider.security.vm_firewalls.create",
  214. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  215. def create(self, label, network, description=None):
  216. OpenStackVMFirewall.assert_valid_resource_label(label)
  217. net_id = network.id if isinstance(network, Network) else network
  218. # We generally simulate a network being associated with a firewall
  219. # by storing the supplied value in the firewall description field that
  220. # is not modifiable after creation; however, because of some networking
  221. # specificity in Nectar, we must also allow an empty network id value.
  222. if not net_id:
  223. net_id = ""
  224. if not description:
  225. description = ""
  226. description += " [{}{}]".format(OpenStackVMFirewall._network_id_tag,
  227. net_id)
  228. sg = self.provider.os_conn.network.create_security_group(
  229. name=label, description=description)
  230. if sg:
  231. return OpenStackVMFirewall(self.provider, sg)
  232. return None
  233. @dispatch(event="provider.security.vm_firewalls.delete",
  234. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  235. def delete(self, vm_firewall):
  236. fw = (vm_firewall if isinstance(vm_firewall, OpenStackVMFirewall)
  237. else self.get(vm_firewall))
  238. if fw:
  239. # pylint:disable=protected-access
  240. fw._vm_firewall.delete(self.provider.os_conn.network)
  241. class OpenStackVMFirewallRuleService(BaseVMFirewallRuleService):
  242. def __init__(self, provider):
  243. super(OpenStackVMFirewallRuleService, self).__init__(provider)
  244. @dispatch(event="provider.security.vm_firewall_rules.list",
  245. priority=BaseVMFirewallRuleService.STANDARD_EVENT_PRIORITY)
  246. def list(self, firewall, limit=None, marker=None):
  247. # pylint:disable=protected-access
  248. rules = [OpenStackVMFirewallRule(firewall, r)
  249. for r in firewall._vm_firewall.security_group_rules]
  250. return ClientPagedResultList(self.provider, rules,
  251. limit=limit, marker=marker)
  252. @dispatch(event="provider.security.vm_firewall_rules.create",
  253. priority=BaseVMFirewallRuleService.STANDARD_EVENT_PRIORITY)
  254. def create(self, firewall, direction, protocol=None, from_port=None,
  255. to_port=None, cidr=None, src_dest_fw=None):
  256. src_dest_fw_id = (src_dest_fw.id if isinstance(src_dest_fw,
  257. OpenStackVMFirewall)
  258. else src_dest_fw)
  259. try:
  260. if direction == TrafficDirection.INBOUND:
  261. os_direction = 'ingress'
  262. elif direction == TrafficDirection.OUTBOUND:
  263. os_direction = 'egress'
  264. else:
  265. raise InvalidValueException("direction", direction)
  266. # pylint:disable=protected-access
  267. rule = self.provider.os_conn.network.create_security_group_rule(
  268. security_group_id=firewall.id,
  269. direction=os_direction,
  270. port_range_max=to_port,
  271. port_range_min=from_port,
  272. protocol=protocol,
  273. remote_ip_prefix=cidr,
  274. remote_group_id=src_dest_fw_id)
  275. firewall.refresh()
  276. return OpenStackVMFirewallRule(firewall, rule.to_dict())
  277. except HttpException as e:
  278. firewall.refresh()
  279. # 409=Conflict, raised for duplicate rule
  280. if e.status_code == 409:
  281. existing = self.find(firewall, direction=direction,
  282. protocol=protocol, from_port=from_port,
  283. to_port=to_port, cidr=cidr,
  284. src_dest_fw_id=src_dest_fw_id)
  285. return existing[0]
  286. else:
  287. raise e
  288. @dispatch(event="provider.security.vm_firewall_rules.delete",
  289. priority=BaseVMFirewallRuleService.STANDARD_EVENT_PRIORITY)
  290. def delete(self, firewall, rule):
  291. rule_id = (rule.id if isinstance(rule, OpenStackVMFirewallRule)
  292. else rule)
  293. self.provider.os_conn.network.delete_security_group_rule(rule_id)
  294. firewall.refresh()
  295. class OpenStackStorageService(BaseStorageService):
  296. def __init__(self, provider):
  297. super(OpenStackStorageService, self).__init__(provider)
  298. # pylint:disable=protected-access
  299. self.service_zone_name = self.provider._get_config_value(
  300. 'os_storage_zone_name', cb_helpers.get_env(
  301. 'OS_STORAGE_ZONE_NAME', self.provider.zone_name))
  302. # Initialize provider services
  303. self._volume_svc = OpenStackVolumeService(self.provider)
  304. self._snapshot_svc = OpenStackSnapshotService(self.provider)
  305. self._bucket_svc = OpenStackBucketService(self.provider)
  306. self._bucket_obj_svc = OpenStackBucketObjectService(self.provider)
  307. @property
  308. def volumes(self):
  309. return self._volume_svc
  310. @property
  311. def snapshots(self):
  312. return self._snapshot_svc
  313. @property
  314. def buckets(self):
  315. return self._bucket_svc
  316. @property
  317. def _bucket_objects(self):
  318. return self._bucket_obj_svc
  319. class OpenStackVolumeService(BaseVolumeService):
  320. def __init__(self, provider):
  321. super(OpenStackVolumeService, self).__init__(provider)
  322. @dispatch(event="provider.storage.volumes.get",
  323. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  324. def get(self, volume_id):
  325. try:
  326. os_vol = self.provider.os_conn.block_storage.get_volume(volume_id)
  327. except (NotFoundException, ResourceNotFound):
  328. log.debug("Volume %s was not found.", volume_id)
  329. return None
  330. if os_vol.availability_zone != self.provider.service_zone_name(self):
  331. log.debug("Volume %s was found in availability zone '%s' while the"
  332. " OpenStack provider is in zone '%s'",
  333. volume_id,
  334. os_vol.availability_zone,
  335. self.provider.service_zone_name(self))
  336. return None
  337. else:
  338. return OpenStackVolume(self.provider, os_vol)
  339. @dispatch(event="provider.storage.volumes.find",
  340. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  341. def find(self, **kwargs):
  342. label = kwargs.pop('label', None)
  343. # All kwargs should have been popped at this time.
  344. if len(kwargs) > 0:
  345. raise InvalidParamException(
  346. "Unrecognised parameters for search: %s. Supported "
  347. "attributes: %s" % (kwargs, 'label'))
  348. log.debug("Searching for an OpenStack Volume with the label %s", label)
  349. cb_vols = [
  350. OpenStackVolume(self.provider, vol)
  351. for vol in self.provider.os_conn.block_storage.volumes(
  352. name=label,
  353. limit=oshelpers.os_result_limit(self.provider),
  354. marker=None)
  355. if vol.availability_zone == self.provider.service_zone_name(self)]
  356. return oshelpers.to_server_paged_list(self.provider, cb_vols)
  357. @dispatch(event="provider.storage.volumes.list",
  358. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  359. def list(self, limit=None, marker=None):
  360. cb_vols = [
  361. OpenStackVolume(self.provider, vol)
  362. for vol in self.provider.os_conn.block_storage.volumes(
  363. limit=oshelpers.os_result_limit(self.provider, limit),
  364. marker=marker)
  365. if vol.availability_zone == self.provider.service_zone_name(self)]
  366. return oshelpers.to_server_paged_list(self.provider, cb_vols, limit)
  367. @dispatch(event="provider.storage.volumes.create",
  368. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  369. def create(self, label, size, snapshot=None, description=None):
  370. OpenStackVolume.assert_valid_resource_label(label)
  371. zone_name = self.provider.service_zone_name(self)
  372. snapshot_id = snapshot.id if isinstance(
  373. snapshot, OpenStackSnapshot) and snapshot else snapshot
  374. os_vol = self.provider.os_conn.block_storage.create_volume(
  375. size=size, name=label, description=description,
  376. availability_zone=zone_name, snapshot_id=snapshot_id)
  377. return OpenStackVolume(self.provider, os_vol)
  378. @dispatch(event="provider.storage.volumes.delete",
  379. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  380. def delete(self, volume):
  381. vol = (volume if isinstance(volume, OpenStackVolume)
  382. else self.get(volume))
  383. if vol:
  384. # pylint:disable=protected-access
  385. self.provider.os_conn.block_storage.delete_volume(vol._volume)
  386. class OpenStackSnapshotService(BaseSnapshotService):
  387. def __init__(self, provider):
  388. super(OpenStackSnapshotService, self).__init__(provider)
  389. @dispatch(event="provider.storage.snapshots.get",
  390. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  391. def get(self, snapshot_id):
  392. try:
  393. return OpenStackSnapshot(
  394. self.provider,
  395. self.provider.os_conn.block_storage.get_snapshot(snapshot_id))
  396. except (NotFoundException, ResourceNotFound):
  397. log.debug("Snapshot %s was not found.", snapshot_id)
  398. return None
  399. @dispatch(event="provider.storage.snapshots.find",
  400. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  401. def find(self, **kwargs):
  402. label = kwargs.pop('label', None)
  403. # All kwargs should have been popped at this time.
  404. if len(kwargs) > 0:
  405. raise InvalidParamException(
  406. "Unrecognised parameters for search: %s. Supported "
  407. "attributes: %s" % (kwargs, 'label'))
  408. search_opts = {'name': label, # TODO: Cinder is ignoring name
  409. 'limit': oshelpers.os_result_limit(self.provider),
  410. 'marker': None}
  411. log.debug("Searching for an OpenStack snapshot with the following "
  412. "params: %s", search_opts)
  413. cb_snaps = [
  414. OpenStackSnapshot(self.provider, snap) for
  415. snap in self.provider.os_conn.block_storage.snapshots(
  416. **search_opts)
  417. if snap.name == label]
  418. return oshelpers.to_server_paged_list(self.provider, cb_snaps)
  419. @dispatch(event="provider.storage.snapshots.list",
  420. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  421. def list(self, limit=None, marker=None):
  422. cb_snaps = [
  423. OpenStackSnapshot(self.provider, snap)
  424. for snap in self.provider.os_conn.block_storage.snapshots(
  425. limit=oshelpers.os_result_limit(self.provider, limit),
  426. marker=marker)
  427. ]
  428. return oshelpers.to_server_paged_list(self.provider, cb_snaps, limit)
  429. @dispatch(event="provider.storage.snapshots.create",
  430. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  431. def create(self, label, volume, description=None):
  432. OpenStackSnapshot.assert_valid_resource_label(label)
  433. volume_id = (volume.id if isinstance(volume, OpenStackVolume)
  434. else volume)
  435. os_snap = self.provider.os_conn.block_storage.create_snapshot(
  436. volume_id=volume_id, name=label,
  437. description=description)
  438. return OpenStackSnapshot(self.provider, os_snap)
  439. @dispatch(event="provider.storage.snapshots.delete",
  440. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  441. def delete(self, snapshot):
  442. s = (snapshot if isinstance(snapshot, OpenStackSnapshot) else
  443. self.get(snapshot))
  444. if s:
  445. # pylint:disable=protected-access
  446. self.provider.os_conn.block_storage.delete_snapshot(s._snapshot)
  447. class OpenStackBucketService(BaseBucketService):
  448. def __init__(self, provider):
  449. super(OpenStackBucketService, self).__init__(provider)
  450. @dispatch(event="provider.storage.buckets.get",
  451. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  452. def get(self, bucket_id):
  453. """
  454. Returns a bucket given its ID. Returns ``None`` if the bucket
  455. does not exist.
  456. """
  457. _, container_list = self.provider.swift.get_account(
  458. prefix=bucket_id)
  459. if container_list:
  460. return OpenStackBucket(self.provider,
  461. next((c for c in container_list
  462. if c['name'] == bucket_id), None))
  463. else:
  464. log.debug("Bucket %s was not found.", bucket_id)
  465. return None
  466. @dispatch(event="provider.storage.buckets.find",
  467. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  468. def find(self, **kwargs):
  469. name = kwargs.pop('name', None)
  470. # All kwargs should have been popped at this time.
  471. if len(kwargs) > 0:
  472. raise InvalidParamException(
  473. "Unrecognised parameters for search: %s. Supported "
  474. "attributes: %s" % (kwargs, 'name'))
  475. _, container_list = self.provider.swift.get_account()
  476. cb_buckets = [OpenStackBucket(self.provider, c)
  477. for c in container_list
  478. if name in c.get("name")]
  479. return oshelpers.to_server_paged_list(self.provider, cb_buckets)
  480. @dispatch(event="provider.storage.buckets.list",
  481. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  482. def list(self, limit=None, marker=None):
  483. _, container_list = self.provider.swift.get_account(
  484. limit=oshelpers.os_result_limit(self.provider, limit),
  485. marker=marker)
  486. cb_buckets = [OpenStackBucket(self.provider, c)
  487. for c in container_list]
  488. return oshelpers.to_server_paged_list(self.provider, cb_buckets, limit)
  489. @dispatch(event="provider.storage.buckets.create",
  490. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  491. def create(self, name, location=None):
  492. OpenStackBucket.assert_valid_resource_name(name)
  493. location = location or self.provider.region_name
  494. try:
  495. self.provider.swift.head_container(name)
  496. raise DuplicateResourceException(
  497. 'Bucket already exists with name {0}'.format(name))
  498. except SwiftClientException:
  499. self.provider.swift.put_container(name)
  500. return self.get(name)
  501. @dispatch(event="provider.storage.buckets.delete",
  502. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  503. def delete(self, bucket):
  504. b_id = bucket.id if isinstance(bucket, OpenStackBucket) else bucket
  505. self.provider.swift.delete_container(b_id)
  506. class OpenStackBucketObjectService(BaseBucketObjectService):
  507. def __init__(self, provider):
  508. super(OpenStackBucketObjectService, self).__init__(provider)
  509. def get(self, bucket, name):
  510. """
  511. Retrieve a given object from this bucket.
  512. """
  513. # Swift always returns a reference for the container first,
  514. # followed by a list containing references to objects.
  515. _, object_list = self.provider.swift.get_container(
  516. bucket.name, prefix=name)
  517. # Loop through list of objects looking for an exact name vs. a prefix
  518. for obj in object_list:
  519. if obj.get('name') == name:
  520. return OpenStackBucketObject(self.provider,
  521. bucket,
  522. obj)
  523. return None
  524. def list(self, bucket, limit=None, marker=None, prefix=None):
  525. """
  526. List all objects within this bucket.
  527. :rtype: BucketObject
  528. :return: List of all available BucketObjects within this bucket.
  529. """
  530. _, object_list = self.provider.swift.get_container(
  531. bucket.name,
  532. limit=oshelpers.os_result_limit(self.provider, limit),
  533. marker=marker, prefix=prefix)
  534. cb_objects = [OpenStackBucketObject(
  535. self.provider, bucket, obj) for obj in object_list]
  536. return oshelpers.to_server_paged_list(
  537. self.provider,
  538. cb_objects,
  539. limit)
  540. def find(self, bucket, **kwargs):
  541. _, obj_list = self.provider.swift.get_container(bucket.name)
  542. cb_objs = [OpenStackBucketObject(self.provider, bucket, obj)
  543. for obj in obj_list]
  544. filters = ['name']
  545. matches = cb_helpers.generic_find(filters, kwargs, cb_objs)
  546. return ClientPagedResultList(self.provider, list(matches))
  547. def create(self, bucket, object_name):
  548. self.provider.swift.put_object(bucket.name, object_name, None)
  549. return self.get(bucket, object_name)
  550. class OpenStackComputeService(BaseComputeService):
  551. def __init__(self, provider):
  552. super(OpenStackComputeService, self).__init__(provider)
  553. self._vm_type_svc = OpenStackVMTypeService(self.provider)
  554. self._instance_svc = OpenStackInstanceService(self.provider)
  555. self._region_svc = OpenStackRegionService(self.provider)
  556. self._images_svc = OpenStackImageService(self.provider)
  557. # Region service must be defined before invoking the following
  558. # pylint:disable=protected-access
  559. self.service_zone_name = self.provider._get_config_value(
  560. 'os_compute_zone_name',
  561. cb_helpers.get_env(
  562. 'OS_COMPUTE_ZONE_NAME',
  563. self.provider._zone_name or
  564. self.regions.current.default_zone.name))
  565. @property
  566. def images(self):
  567. return self._images_svc
  568. @property
  569. def vm_types(self):
  570. return self._vm_type_svc
  571. @property
  572. def instances(self):
  573. return self._instance_svc
  574. @property
  575. def regions(self):
  576. return self._region_svc
  577. class OpenStackImageService(BaseImageService):
  578. def __init__(self, provider):
  579. super(OpenStackImageService, self).__init__(provider)
  580. def get(self, image_id):
  581. """
  582. Returns an Image given its id
  583. """
  584. log.debug("Getting OpenStack Image with the id: %s", image_id)
  585. try:
  586. return OpenStackMachineImage(
  587. self.provider, self.provider.os_conn.image.get_image(image_id))
  588. except (NotFoundException, ResourceNotFound):
  589. log.debug("Image %s not found", image_id)
  590. return None
  591. def find(self, **kwargs):
  592. filters = ['label']
  593. obj_list = self
  594. return cb_helpers.generic_find(filters, kwargs, obj_list)
  595. def list(self, filter_by_owner=True, limit=None, marker=None):
  596. """
  597. List all images.
  598. """
  599. project_id = None
  600. if filter_by_owner:
  601. project_id = self.provider.os_conn.session.get_project_id()
  602. os_images = self.provider.os_conn.image.images(
  603. owner=project_id,
  604. limit=oshelpers.os_result_limit(self.provider, limit),
  605. marker=marker)
  606. cb_images = [
  607. OpenStackMachineImage(self.provider, img)
  608. for img in os_images]
  609. return oshelpers.to_server_paged_list(self.provider, cb_images, limit)
  610. class OpenStackInstanceService(BaseInstanceService):
  611. def __init__(self, provider):
  612. super(OpenStackInstanceService, self).__init__(provider)
  613. def _to_block_device_mapping(self, launch_config):
  614. """
  615. Extracts block device mapping information
  616. from a launch config and constructs a BlockDeviceMappingV2
  617. object.
  618. """
  619. bdm = []
  620. for device in launch_config.block_devices:
  621. bdm_dict = dict()
  622. if device.is_volume:
  623. bdm_dict['destination_type'] = 'volume'
  624. if device.is_root:
  625. bdm_dict['device_name'] = '/dev/sda'
  626. bdm_dict['boot_index'] = 0
  627. if isinstance(device.source, Snapshot):
  628. bdm_dict['source_type'] = 'snapshot'
  629. bdm_dict['uuid'] = device.source.id
  630. elif isinstance(device.source, Volume):
  631. bdm_dict['source_type'] = 'volume'
  632. bdm_dict['uuid'] = device.source.id
  633. elif isinstance(device.source, MachineImage):
  634. bdm_dict['source_type'] = 'image'
  635. bdm_dict['uuid'] = device.source.id
  636. else:
  637. bdm_dict['source_type'] = 'blank'
  638. if device.delete_on_terminate is not None:
  639. bdm_dict[
  640. 'delete_on_termination'] = device.delete_on_terminate
  641. if device.size:
  642. bdm_dict['volume_size'] = device.size
  643. else:
  644. bdm_dict['destination_type'] = 'local'
  645. bdm_dict['source_type'] = 'blank'
  646. bdm_dict['delete_on_termination'] = True
  647. bdm.append(bdm_dict)
  648. return bdm
  649. def _has_root_device(self, launch_config):
  650. if not launch_config:
  651. return False
  652. for device in launch_config.block_devices:
  653. if device.is_root:
  654. return True
  655. return False
  656. def create_launch_config(self):
  657. return BaseLaunchConfig(self.provider)
  658. @dispatch(event="provider.compute.instances.create",
  659. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  660. def create(self, label, image, vm_type, subnet,
  661. key_pair=None, vm_firewalls=None, user_data=None,
  662. launch_config=None, **kwargs):
  663. OpenStackInstance.assert_valid_resource_label(label)
  664. image_id = image.id if isinstance(image, MachineImage) else image
  665. if isinstance(vm_type, VMType):
  666. vm_size = vm_type.id
  667. else:
  668. vm_type_obj = self.provider.compute.vm_types.find(name=vm_type)
  669. if not vm_type_obj:
  670. raise CloudBridgeBaseException(
  671. "Could not find vm type with name {0}".format(vm_type))
  672. vm_size = vm_type_obj[0].id
  673. if isinstance(subnet, Subnet):
  674. subnet_id = subnet.id
  675. net_id = subnet.network_id
  676. else:
  677. subnet_id = subnet
  678. net_id = (self.provider.networking.subnets
  679. .get(subnet_id).network_id
  680. if subnet_id else None)
  681. zone_name = self.provider.service_zone_name(self)
  682. key_pair_name = key_pair.name if \
  683. isinstance(key_pair, KeyPair) else key_pair
  684. bdm = None
  685. if launch_config:
  686. bdm = self._to_block_device_mapping(launch_config)
  687. # Security groups must be passed in as a list of IDs and attached to a
  688. # port if a port is being created. Otherwise, the security groups must
  689. # be passed in as a list of names to the servers.create() call.
  690. # OpenStack will respect the port's security groups first and then
  691. # fall-back to the named security groups.
  692. sg_name_list = []
  693. nics = None
  694. if subnet_id:
  695. log.debug("Creating network port for %s in subnet: %s",
  696. label, subnet_id)
  697. sg_list = []
  698. if vm_firewalls:
  699. if isinstance(vm_firewalls, list) and \
  700. isinstance(vm_firewalls[0], VMFirewall):
  701. sg_list = vm_firewalls
  702. else:
  703. sg_list = (self.provider.security.vm_firewalls
  704. .find(label=sg) for sg in vm_firewalls)
  705. sg_list = (sg[0] for sg in sg_list if sg)
  706. sg_id_list = [sg.id for sg in sg_list]
  707. port_def = {
  708. "port": {
  709. "admin_state_up": True,
  710. "name": OpenStackInstance._generate_name_from_label(
  711. label, 'cb-port'),
  712. "network_id": net_id,
  713. "fixed_ips": [{"subnet_id": subnet_id}],
  714. "security_groups": sg_id_list
  715. }
  716. }
  717. port_id = self.provider.neutron.create_port(port_def)['port']['id']
  718. nics = [{'net-id': net_id, 'port-id': port_id}]
  719. else:
  720. if vm_firewalls:
  721. if isinstance(vm_firewalls, list) and \
  722. isinstance(vm_firewalls[0], VMFirewall):
  723. sg_name_list = [sg.name for sg in vm_firewalls]
  724. else:
  725. sg_list = (self.provider.security.vm_firewalls.get(sg)
  726. for sg in vm_firewalls)
  727. sg_name_list = (sg[0].name for sg in sg_list if sg)
  728. log.debug("Launching in subnet %s", subnet_id)
  729. os_instance = self.provider.nova.servers.create(
  730. label,
  731. None if self._has_root_device(launch_config) else image_id,
  732. vm_size,
  733. min_count=1,
  734. max_count=1,
  735. availability_zone=zone_name,
  736. key_name=key_pair_name,
  737. security_groups=sg_name_list,
  738. userdata=str(user_data) or None,
  739. block_device_mapping_v2=bdm,
  740. nics=nics)
  741. return OpenStackInstance(self.provider, os_instance)
  742. @dispatch(event="provider.compute.instances.find",
  743. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  744. def find(self, **kwargs):
  745. label = kwargs.pop('label', None)
  746. # All kwargs should have been popped at this time.
  747. if len(kwargs) > 0:
  748. raise InvalidParamException(
  749. "Unrecognised parameters for search: %s. Supported "
  750. "attributes: %s" % (kwargs, 'label'))
  751. search_opts = {'name': label,
  752. 'availability_zone': self.provider
  753. .service_zone_name(self)}
  754. cb_insts = [
  755. OpenStackInstance(self.provider, inst)
  756. for inst in self.provider.nova.servers.list(
  757. search_opts=search_opts,
  758. limit=oshelpers.os_result_limit(self.provider),
  759. marker=None)]
  760. return oshelpers.to_server_paged_list(self.provider, cb_insts)
  761. @dispatch(event="provider.compute.instances.list",
  762. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  763. def list(self, limit=None, marker=None):
  764. """
  765. List all instances.
  766. """
  767. search_opts = {'availability_zone': self.provider
  768. .service_zone_name(self)}
  769. cb_insts = [
  770. OpenStackInstance(self.provider, inst)
  771. for inst in self.provider.nova.servers.list(
  772. search_opts=search_opts,
  773. limit=oshelpers.os_result_limit(self.provider, limit),
  774. marker=marker)]
  775. return oshelpers.to_server_paged_list(self.provider, cb_insts, limit)
  776. @dispatch(event="provider.compute.instances.get",
  777. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  778. def get(self, instance_id):
  779. """
  780. Returns an instance given its id.
  781. """
  782. try:
  783. os_instance = self.provider.nova.servers.get(instance_id)
  784. except NovaNotFound:
  785. log.debug("Instance %s was not found.", instance_id)
  786. return None
  787. if (getattr(os_instance,
  788. 'OS-EXT-AZ:availability_zone', "")
  789. != self.provider.service_zone_name(self)):
  790. log.debug("Instance %s was found in availability zone '%s' while "
  791. "the OpenStack provider is in zone '%s'",
  792. instance_id,
  793. getattr(os_instance, 'OS-EXT-AZ:availability_zone', ""),
  794. self.provider.service_zone_name(self))
  795. return None
  796. return OpenStackInstance(self.provider, os_instance)
  797. @dispatch(event="provider.compute.instances.delete",
  798. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  799. def delete(self, instance):
  800. ins = (instance if isinstance(instance, OpenStackInstance) else
  801. self.get(instance))
  802. if ins:
  803. # pylint:disable=protected-access
  804. os_instance = ins._os_instance
  805. # delete the port we created when launching
  806. # Assumption: it's the first interface in the list
  807. iface_list = os_instance.interface_list()
  808. if iface_list:
  809. self.provider.neutron.delete_port(iface_list[0].port_id)
  810. os_instance.delete()
  811. class OpenStackVMTypeService(BaseVMTypeService):
  812. def __init__(self, provider):
  813. super(OpenStackVMTypeService, self).__init__(provider)
  814. @dispatch(event="provider.compute.vm_types.list",
  815. priority=BaseVMTypeService.STANDARD_EVENT_PRIORITY)
  816. def list(self, limit=None, marker=None):
  817. cb_itypes = [
  818. OpenStackVMType(self.provider, obj)
  819. for obj in self.provider.nova.flavors.list(
  820. limit=oshelpers.os_result_limit(self.provider, limit),
  821. marker=marker)]
  822. return oshelpers.to_server_paged_list(self.provider, cb_itypes, limit)
  823. class OpenStackRegionService(BaseRegionService):
  824. def __init__(self, provider):
  825. super(OpenStackRegionService, self).__init__(provider)
  826. @dispatch(event="provider.compute.regions.get",
  827. priority=BaseRegionService.STANDARD_EVENT_PRIORITY)
  828. def get(self, region_id):
  829. log.debug("Getting OpenStack Region with the id: %s", region_id)
  830. region = (r for r in self if r.id == region_id)
  831. return next(region, None)
  832. @dispatch(event="provider.compute.regions.list",
  833. priority=BaseRegionService.STANDARD_EVENT_PRIORITY)
  834. def list(self, limit=None, marker=None):
  835. # pylint:disable=protected-access
  836. if self.provider._keystone_version == 3:
  837. os_regions = [OpenStackRegion(self.provider, region)
  838. for region in self.provider.keystone.regions.list()]
  839. return ClientPagedResultList(self.provider, os_regions,
  840. limit=limit, marker=marker)
  841. else:
  842. # Keystone v3 onwards supports directly listing regions
  843. # but for v2, this convoluted method is necessary.
  844. regions = (
  845. endpoint.get('region') or endpoint.get('region_id')
  846. for svc in self.provider.keystone.service_catalog.get_data()
  847. for endpoint in svc.get('endpoints', [])
  848. )
  849. regions = set(region for region in regions if region)
  850. os_regions = [OpenStackRegion(self.provider, region)
  851. for region in regions]
  852. return ClientPagedResultList(self.provider, os_regions,
  853. limit=limit, marker=marker)
  854. @property
  855. def current(self):
  856. nova_region = self.provider.nova.client.region_name
  857. return self.get(nova_region) if nova_region else None
  858. class OpenStackNetworkingService(BaseNetworkingService):
  859. def __init__(self, provider):
  860. super(OpenStackNetworkingService, self).__init__(provider)
  861. # pylint:disable=protected-access
  862. self.service_zone_name = self.provider._get_config_value(
  863. 'os_networking_zone_name', cb_helpers.get_env(
  864. 'OS_NETWORKING_ZONE_NAME', self.provider.zone_name))
  865. self._network_service = OpenStackNetworkService(self.provider)
  866. self._subnet_service = OpenStackSubnetService(self.provider)
  867. self._router_service = OpenStackRouterService(self.provider)
  868. self._gateway_service = OpenStackGatewayService(self.provider)
  869. self._floating_ip_service = OpenStackFloatingIPService(self.provider)
  870. @property
  871. def networks(self):
  872. return self._network_service
  873. @property
  874. def subnets(self):
  875. return self._subnet_service
  876. @property
  877. def routers(self):
  878. return self._router_service
  879. @property
  880. def _gateways(self):
  881. return self._gateway_service
  882. @property
  883. def _floating_ips(self):
  884. return self._floating_ip_service
  885. class OpenStackNetworkService(BaseNetworkService):
  886. def __init__(self, provider):
  887. super(OpenStackNetworkService, self).__init__(provider)
  888. @dispatch(event="provider.networking.networks.get",
  889. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  890. def get(self, network_id):
  891. network = (n for n in self if n.id == network_id)
  892. return next(network, None)
  893. @dispatch(event="provider.networking.networks.list",
  894. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  895. def list(self, limit=None, marker=None):
  896. networks = [OpenStackNetwork(self.provider, network)
  897. for network in self.provider.neutron.list_networks()
  898. .get('networks') if network
  899. # If there are no availability zones, keep the network
  900. # in the results list
  901. and (not network.get('availability_zones')
  902. or self.provider.service_zone_name(self)
  903. in network.get('availability_zones'))]
  904. return ClientPagedResultList(self.provider, networks,
  905. limit=limit, marker=marker)
  906. @dispatch(event="provider.networking.networks.find",
  907. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  908. def find(self, **kwargs):
  909. obj_list = self
  910. filters = ['label']
  911. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  912. return ClientPagedResultList(self._provider, list(matches))
  913. @dispatch(event="provider.networking.networks.create",
  914. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  915. def create(self, label, cidr_block):
  916. OpenStackNetwork.assert_valid_resource_label(label)
  917. net_info = {'name': label or ""}
  918. network = self.provider.neutron.create_network({'network': net_info})
  919. cb_net = OpenStackNetwork(self.provider, network.get('network'))
  920. return cb_net
  921. @dispatch(event="provider.networking.networks.delete",
  922. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  923. def delete(self, network):
  924. network = (network if isinstance(network, OpenStackNetwork) else
  925. self.get(network))
  926. if not network:
  927. return
  928. if not network.external and network.id in str(
  929. self.provider.neutron.list_networks()):
  930. # If there are ports associated with the network, it won't delete
  931. ports = self.provider.neutron.list_ports(
  932. network_id=network.id).get('ports', [])
  933. for port in ports:
  934. try:
  935. self.provider.neutron.delete_port(port.get('id'))
  936. except PortNotFoundClient:
  937. # Ports could have already been deleted if instances
  938. # are terminated etc. so exceptions can be safely ignored
  939. pass
  940. self.provider.neutron.delete_network(network.id)
  941. class OpenStackSubnetService(BaseSubnetService):
  942. def __init__(self, provider):
  943. super(OpenStackSubnetService, self).__init__(provider)
  944. @dispatch(event="provider.networking.subnets.get",
  945. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  946. def get(self, subnet_id):
  947. subnet = (s for s in self if s.id == subnet_id)
  948. return next(subnet, None)
  949. @dispatch(event="provider.networking.subnets.list",
  950. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  951. def list(self, network=None, limit=None, marker=None):
  952. if network:
  953. network_id = (network.id if isinstance(network, OpenStackNetwork)
  954. else network)
  955. subnets = [subnet for subnet in self if network_id ==
  956. subnet.network_id]
  957. else:
  958. subnets = [OpenStackSubnet(self.provider, subnet) for subnet in
  959. self.provider.neutron.list_subnets().get('subnets', [])]
  960. return ClientPagedResultList(self.provider, subnets,
  961. limit=limit, marker=marker)
  962. @dispatch(event="provider.networking.subnets.create",
  963. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  964. def create(self, label, network, cidr_block):
  965. OpenStackSubnet.assert_valid_resource_label(label)
  966. network_id = (network.id if isinstance(network, OpenStackNetwork)
  967. else network)
  968. subnet_info = {'name': label, 'network_id': network_id,
  969. 'cidr': cidr_block, 'ip_version': 4}
  970. subnet = (self.provider.neutron.create_subnet({'subnet': subnet_info})
  971. .get('subnet'))
  972. cb_subnet = OpenStackSubnet(self.provider, subnet)
  973. return cb_subnet
  974. @dispatch(event="provider.networking.subnets.delete",
  975. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  976. def delete(self, subnet):
  977. sn_id = subnet.id if isinstance(subnet, OpenStackSubnet) else subnet
  978. self.provider.neutron.delete_subnet(sn_id)
  979. def get_or_create_default(self):
  980. try:
  981. sn = self.find(label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL)
  982. if sn:
  983. return sn[0]
  984. # No default subnet look for default network, then create subnet
  985. net = self.provider.networking.networks.get_or_create_default()
  986. sn = self.provider.networking.subnets.create(
  987. label=OpenStackSubnet.CB_DEFAULT_SUBNET_LABEL,
  988. cidr_block=OpenStackSubnet.CB_DEFAULT_SUBNET_IPV4RANGE,
  989. network=net)
  990. router = self.provider.networking.routers.get_or_create_default(
  991. net)
  992. router.attach_subnet(sn)
  993. gateway = net.gateways.get_or_create()
  994. router.attach_gateway(gateway)
  995. return sn
  996. except NeutronClientException:
  997. return None
  998. class OpenStackRouterService(BaseRouterService):
  999. def __init__(self, provider):
  1000. super(OpenStackRouterService, self).__init__(provider)
  1001. @dispatch(event="provider.networking.routers.get",
  1002. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1003. def get(self, router_id):
  1004. router = self.provider.os_conn.get_router(router_id)
  1005. if not router:
  1006. log.debug("Router %s was not found.", router_id)
  1007. return None
  1008. elif self.provider.service_zone_name(self) \
  1009. not in router.availability_zones:
  1010. log.debug("Router %s was found in availability zone '%s' while the"
  1011. " OpenStack provider is in zone '%s'",
  1012. router_id,
  1013. router.availability_zones,
  1014. self.provider.service_zone_name(self))
  1015. return None
  1016. return OpenStackRouter(self.provider, router)
  1017. @dispatch(event="provider.networking.routers.list",
  1018. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1019. def list(self, limit=None, marker=None):
  1020. routers = self.provider.os_conn.list_routers()
  1021. os_routers = [OpenStackRouter(self.provider, r) for r in routers
  1022. if self.provider.service_zone_name(self)
  1023. in r.availability_zones]
  1024. return ClientPagedResultList(self.provider, os_routers, limit=limit,
  1025. marker=marker)
  1026. @dispatch(event="provider.networking.routers.find",
  1027. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1028. def find(self, **kwargs):
  1029. obj_list = self
  1030. filters = ['label']
  1031. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  1032. return ClientPagedResultList(self._provider, list(matches))
  1033. @dispatch(event="provider.networking.routers.create",
  1034. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1035. def create(self, label, network):
  1036. """Parameter ``network`` is not used by OpenStack."""
  1037. router = self.provider.os_conn.create_router(name=label)
  1038. return OpenStackRouter(self.provider, router)
  1039. @dispatch(event="provider.networking.routers.delete",
  1040. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1041. def delete(self, router):
  1042. r_id = router.id if isinstance(router, OpenStackRouter) else router
  1043. self.provider.os_conn.delete_router(r_id)
  1044. class OpenStackGatewayService(BaseGatewayService):
  1045. """For OpenStack, an internet gateway is a just an 'external' network."""
  1046. def __init__(self, provider):
  1047. super(OpenStackGatewayService, self).__init__(provider)
  1048. def _check_fip_connectivity(self, network, external_net):
  1049. # Due to current limitations in OpenStack:
  1050. # https://bugs.launchpad.net/neutron/+bug/1743480, it's not
  1051. # possible to differentiate between floating ip networks and provider
  1052. # external networks. Therefore, we systematically step through
  1053. # all available networks and perform an assignment test to infer valid
  1054. # floating ip nets.
  1055. dummy_router = self._provider.networking.routers.create(
  1056. label='cb-conn-test-router', network=network)
  1057. with cb_helpers.cleanup_action(lambda: dummy_router.delete()):
  1058. try:
  1059. dummy_router.attach_gateway(external_net)
  1060. return True
  1061. except Exception:
  1062. return False
  1063. @dispatch(event="provider.networking.gateways.get_or_create",
  1064. priority=BaseGatewayService.STANDARD_EVENT_PRIORITY)
  1065. def get_or_create(self, network):
  1066. """For OS, inet gtw is any net that has `external` property set."""
  1067. external_nets = (n for n in self._provider.networking.networks
  1068. if n.external)
  1069. for net in external_nets:
  1070. if self._check_fip_connectivity(network, net):
  1071. return OpenStackInternetGateway(self._provider, net)
  1072. return None
  1073. @dispatch(event="provider.networking.gateways.delete",
  1074. priority=BaseGatewayService.STANDARD_EVENT_PRIORITY)
  1075. def delete(self, network, gateway):
  1076. pass
  1077. @dispatch(event="provider.networking.gateways.list",
  1078. priority=BaseGatewayService.STANDARD_EVENT_PRIORITY)
  1079. def list(self, network, limit=None, marker=None):
  1080. log.debug("OpenStack listing of all current internet gateways")
  1081. igl = [OpenStackInternetGateway(self._provider, n)
  1082. for n in self._provider.networking.networks
  1083. if n.external and self._check_fip_connectivity(network, n)]
  1084. return ClientPagedResultList(self._provider, igl, limit=limit,
  1085. marker=marker)
  1086. class OpenStackFloatingIPService(BaseFloatingIPService):
  1087. def __init__(self, provider):
  1088. super(OpenStackFloatingIPService, self).__init__(provider)
  1089. @dispatch(event="provider.networking.floating_ips.get",
  1090. priority=BaseFloatingIPService.STANDARD_EVENT_PRIORITY)
  1091. def get(self, gateway, fip_id):
  1092. try:
  1093. return OpenStackFloatingIP(
  1094. self.provider,
  1095. self.provider.os_conn.network.get_ip(fip_id))
  1096. except (ResourceNotFound, NotFoundException):
  1097. log.debug("Floating IP %s not found.", fip_id)
  1098. return None
  1099. @dispatch(event="provider.networking.floating_ips.list",
  1100. priority=BaseFloatingIPService.STANDARD_EVENT_PRIORITY)
  1101. def list(self, gateway, limit=None, marker=None):
  1102. fips = [OpenStackFloatingIP(self.provider, fip)
  1103. for fip in self.provider.os_conn.network.ips(
  1104. floating_network_id=gateway.id
  1105. )]
  1106. return ClientPagedResultList(self.provider, fips,
  1107. limit=limit, marker=marker)
  1108. @dispatch(event="provider.networking.floating_ips.create",
  1109. priority=BaseFloatingIPService.STANDARD_EVENT_PRIORITY)
  1110. def create(self, gateway):
  1111. return OpenStackFloatingIP(
  1112. self.provider, self.provider.os_conn.network.create_ip(
  1113. floating_network_id=gateway.id))
  1114. @dispatch(event="provider.networking.floating_ips.delete",
  1115. priority=BaseFloatingIPService.STANDARD_EVENT_PRIORITY)
  1116. def delete(self, gateway, fip):
  1117. if isinstance(fip, OpenStackFloatingIP):
  1118. # pylint:disable=protected-access
  1119. os_ip = fip._ip
  1120. else:
  1121. try:
  1122. os_ip = self.provider.os_conn.network.get_ip(fip)
  1123. except (ResourceNotFound, NotFoundException):
  1124. log.debug("Floating IP %s not found.", fip)
  1125. return True
  1126. os_ip.delete(self._provider.os_conn.network)
  1127. class OpenStackDnsService(BaseDnsService):
  1128. def __init__(self, provider):
  1129. super(OpenStackDnsService, self).__init__(provider)
  1130. # Initialize provider services
  1131. self._zone_svc = OpenStackDnsZoneService(self.provider)
  1132. self._record_svc = OpenStackDnsRecordService(self.provider)
  1133. @property
  1134. def host_zones(self):
  1135. return self._zone_svc
  1136. @property
  1137. def _records(self):
  1138. return self._record_svc
  1139. class OpenStackDnsZoneService(BaseDnsZoneService):
  1140. def __init__(self, provider):
  1141. super(OpenStackDnsZoneService, self).__init__(provider)
  1142. @dispatch(event="provider.dns.host_zones.get",
  1143. priority=BaseDnsZoneService.STANDARD_EVENT_PRIORITY)
  1144. def get(self, dns_zone_id):
  1145. try:
  1146. return OpenStackDnsZone(
  1147. self.provider,
  1148. self.provider.os_conn.dns.get_zone(dns_zone_id))
  1149. except (ResourceNotFound, NotFoundException, BadRequestException):
  1150. log.debug("Dns Zone %s not found.", dns_zone_id)
  1151. return None
  1152. @dispatch(event="provider.dns.host_zones.list",
  1153. priority=BaseDnsZoneService.STANDARD_EVENT_PRIORITY)
  1154. def list(self, limit=None, marker=None):
  1155. zones = [OpenStackDnsZone(self.provider, zone)
  1156. for zone in self.provider.os_conn.dns.zones()]
  1157. return ClientPagedResultList(self.provider, zones,
  1158. limit=limit, marker=marker)
  1159. @dispatch(event="provider.dns.host_zones.find",
  1160. priority=BaseDnsZoneService.STANDARD_EVENT_PRIORITY)
  1161. def find(self, **kwargs):
  1162. filters = ['name']
  1163. matches = cb_helpers.generic_find(filters, kwargs, self)
  1164. return ClientPagedResultList(self.provider, list(matches),
  1165. limit=None, marker=None)
  1166. @dispatch(event="provider.dns.host_zones.create",
  1167. priority=BaseDnsZoneService.STANDARD_EVENT_PRIORITY)
  1168. def create(self, name, admin_email):
  1169. OpenStackDnsZone.assert_valid_resource_name(name)
  1170. return OpenStackDnsZone(
  1171. self.provider, self.provider.os_conn.dns.create_zone(
  1172. name=self._get_fully_qualified_dns(name),
  1173. email=admin_email, ttl=3600))
  1174. @dispatch(event="provider.dns.host_zones.delete",
  1175. priority=BaseDnsZoneService.STANDARD_EVENT_PRIORITY)
  1176. def delete(self, dns_zone):
  1177. zone_id = (dns_zone.id if isinstance(dns_zone, OpenStackDnsZone)
  1178. else dns_zone)
  1179. if zone_id:
  1180. self.provider.os_conn.dns.delete_zone(zone_id)
  1181. class OpenStackDnsRecordService(BaseDnsRecordService):
  1182. def __init__(self, provider):
  1183. super(OpenStackDnsRecordService, self).__init__(provider)
  1184. def _to_resource_records(self, data, rec_type):
  1185. """
  1186. Converts a record to what OpenStack expects. For example,
  1187. OpenStack expects a fully qualified name for all CNAME records.
  1188. """
  1189. if isinstance(data, list):
  1190. records = data
  1191. else:
  1192. records = [data]
  1193. return [self._standardize_record(r, rec_type) for r in records]
  1194. def get(self, dns_zone, rec_id):
  1195. try:
  1196. return OpenStackDnsRecord(
  1197. self.provider, dns_zone,
  1198. self.provider.os_conn.dns.get_recordset(rec_id, dns_zone.id))
  1199. except (ResourceNotFound, NotFoundException, BadRequestException):
  1200. log.debug("Dns Record %s not found.", rec_id)
  1201. return None
  1202. def list(self, dns_zone, limit=None, marker=None):
  1203. recs = [OpenStackDnsRecord(self.provider, dns_zone, rec)
  1204. for rec in self.provider.os_conn.dns.recordsets(dns_zone.id)]
  1205. return ClientPagedResultList(self.provider, recs,
  1206. limit=limit, marker=marker)
  1207. def find(self, dns_zone, **kwargs):
  1208. filters = ['name']
  1209. matches = cb_helpers.generic_find(filters, kwargs, dns_zone.records)
  1210. return ClientPagedResultList(self.provider, list(matches),
  1211. limit=None, marker=None)
  1212. def create(self, dns_zone, name, type, data, ttl=None):
  1213. OpenStackDnsZone.assert_valid_resource_name(name)
  1214. return OpenStackDnsRecord(
  1215. self.provider, dns_zone,
  1216. self.provider.os_conn.dns.create_recordset(
  1217. zone=dns_zone.id, name=name, type=type,
  1218. records=self._to_resource_records(data, type),
  1219. ttl=ttl or 3600))
  1220. def delete(self, dns_zone, record):
  1221. rec_id = (record.id if isinstance(record, OpenStackDnsRecord)
  1222. else record)
  1223. if rec_id:
  1224. self.provider.os_conn.dns.delete_recordset(
  1225. rec_id, zone=dns_zone.id)