services.py 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. import base64
  2. import logging
  3. import uuid
  4. from azure.common import AzureException
  5. from azure.mgmt.compute.models import DiskCreateOption
  6. from msrestazure.azure_exceptions import CloudError
  7. import cloudbridge.cloud.base.helpers as cb_helpers
  8. from cloudbridge.cloud.base.middleware import dispatch
  9. from cloudbridge.cloud.base.resources import ClientPagedResultList
  10. from cloudbridge.cloud.base.resources import ServerPagedResultList
  11. from cloudbridge.cloud.base.services import BaseBucketObjectService
  12. from cloudbridge.cloud.base.services import BaseBucketService
  13. from cloudbridge.cloud.base.services import BaseComputeService
  14. from cloudbridge.cloud.base.services import BaseFloatingIPService
  15. from cloudbridge.cloud.base.services import BaseGatewayService
  16. from cloudbridge.cloud.base.services import BaseImageService
  17. from cloudbridge.cloud.base.services import BaseInstanceService
  18. from cloudbridge.cloud.base.services import BaseKeyPairService
  19. from cloudbridge.cloud.base.services import BaseNetworkService
  20. from cloudbridge.cloud.base.services import BaseNetworkingService
  21. from cloudbridge.cloud.base.services import BaseRegionService
  22. from cloudbridge.cloud.base.services import BaseRouterService
  23. from cloudbridge.cloud.base.services import BaseSecurityService
  24. from cloudbridge.cloud.base.services import BaseSnapshotService
  25. from cloudbridge.cloud.base.services import BaseStorageService
  26. from cloudbridge.cloud.base.services import BaseSubnetService
  27. from cloudbridge.cloud.base.services import BaseVMFirewallRuleService
  28. from cloudbridge.cloud.base.services import BaseVMFirewallService
  29. from cloudbridge.cloud.base.services import BaseVMTypeService
  30. from cloudbridge.cloud.base.services import BaseVolumeService
  31. from cloudbridge.cloud.interfaces.exceptions import DuplicateResourceException
  32. from cloudbridge.cloud.interfaces.exceptions import InvalidParamException
  33. from cloudbridge.cloud.interfaces.exceptions import InvalidValueException
  34. from cloudbridge.cloud.interfaces.resources import MachineImage
  35. from cloudbridge.cloud.interfaces.resources import Network
  36. from cloudbridge.cloud.interfaces.resources import PlacementZone
  37. from cloudbridge.cloud.interfaces.resources import Snapshot
  38. from cloudbridge.cloud.interfaces.resources import TrafficDirection
  39. from cloudbridge.cloud.interfaces.resources import VMFirewall
  40. from cloudbridge.cloud.interfaces.resources import VMType
  41. from cloudbridge.cloud.interfaces.resources import Volume
  42. from .resources import AzureBucket
  43. from .resources import AzureBucketObject
  44. from .resources import AzureFloatingIP
  45. from .resources import AzureInstance
  46. from .resources import AzureInternetGateway
  47. from .resources import AzureKeyPair
  48. from .resources import AzureLaunchConfig
  49. from .resources import AzureMachineImage
  50. from .resources import AzureNetwork
  51. from .resources import AzureRegion
  52. from .resources import AzureRouter
  53. from .resources import AzureSnapshot
  54. from .resources import AzureSubnet
  55. from .resources import AzureVMFirewall
  56. from .resources import AzureVMFirewallRule
  57. from .resources import AzureVMType
  58. from .resources import AzureVolume
  59. log = logging.getLogger(__name__)
  60. class AzureSecurityService(BaseSecurityService):
  61. def __init__(self, provider):
  62. super(AzureSecurityService, self).__init__(provider)
  63. # Initialize provider services
  64. self._key_pairs = AzureKeyPairService(provider)
  65. self._vm_firewalls = AzureVMFirewallService(provider)
  66. self._vm_firewall_rules = AzureVMFirewallRuleService(provider)
  67. class AzureVMFirewallService(BaseVMFirewallService):
  68. def __init__(self, provider):
  69. super(AzureVMFirewallService, self).__init__(provider)
  70. @dispatch(event="provider.security.vm_firewalls.get",
  71. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  72. def get(self, vm_firewall_id):
  73. try:
  74. fws = self.provider.azure_client.get_vm_firewall(vm_firewall_id)
  75. return AzureVMFirewall(self.provider, fws)
  76. except (CloudError, InvalidValueException) as cloud_error:
  77. # Azure raises the cloud error if the resource not available
  78. log.exception(cloud_error)
  79. return None
  80. @dispatch(event="provider.security.vm_firewalls.list",
  81. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  82. def list(self, limit=None, marker=None):
  83. fws = [AzureVMFirewall(self.provider, fw)
  84. for fw in self.provider.azure_client.list_vm_firewall()]
  85. return ClientPagedResultList(self.provider, fws, limit, marker)
  86. @cb_helpers.deprecated_alias(network_id='network')
  87. @dispatch(event="provider.security.vm_firewalls.create",
  88. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  89. def create(self, label, network, description=None):
  90. AzureVMFirewall.assert_valid_resource_label(label)
  91. name = AzureVMFirewall._generate_name_from_label(label, "cb-fw")
  92. net = network.id if isinstance(network, Network) else network
  93. parameters = {"location": self.provider.region_name,
  94. "tags": {'Label': label,
  95. 'network_id': net}}
  96. if description:
  97. parameters['tags'].update(Description=description)
  98. fw = self.provider.azure_client.create_vm_firewall(name,
  99. parameters)
  100. # Add default rules to negate azure default rules.
  101. # See: https://github.com/CloudVE/cloudbridge/issues/106
  102. # pylint:disable=protected-access
  103. for rule in fw.default_security_rules:
  104. rule_name = "cb-override-" + rule.name
  105. # Transpose rules to priority 4001 onwards, because
  106. # only 0-4096 are allowed for custom rules
  107. rule.priority = rule.priority - 61440
  108. rule.access = "Deny"
  109. self.provider.azure_client.create_vm_firewall_rule(
  110. fw.id, rule_name, rule)
  111. # Add a new custom rule allowing all outbound traffic to the internet
  112. parameters = {"priority": 3000,
  113. "protocol": "*",
  114. "source_port_range": "*",
  115. "source_address_prefix": "*",
  116. "destination_port_range": "*",
  117. "destination_address_prefix": "Internet",
  118. "access": "Allow",
  119. "direction": "Outbound"}
  120. result = self.provider.azure_client.create_vm_firewall_rule(
  121. fw.id, "cb-default-internet-outbound", parameters)
  122. fw.security_rules.append(result)
  123. cb_fw = AzureVMFirewall(self.provider, fw)
  124. return cb_fw
  125. @dispatch(event="provider.security.vm_firewalls.delete",
  126. priority=BaseVMFirewallService.STANDARD_EVENT_PRIORITY)
  127. def delete(self, vmf):
  128. fw_id = vmf.id if isinstance(vmf, AzureVMFirewall) else vmf
  129. self.provider.azure_client.delete_vm_firewall(fw_id)
  130. class AzureVMFirewallRuleService(BaseVMFirewallRuleService):
  131. def __init__(self, provider):
  132. super(AzureVMFirewallRuleService, self).__init__(provider)
  133. def list(self, firewall, limit=None, marker=None):
  134. # Filter out firewall rules with priority < 3500 because values
  135. # between 3500 and 4096 are assumed to be owned by cloudbridge
  136. # default rules.
  137. # pylint:disable=protected-access
  138. rules = [AzureVMFirewallRule(firewall, rule) for rule
  139. in firewall._vm_firewall.security_rules
  140. if rule.priority < 3500]
  141. return ClientPagedResultList(self._provider, rules,
  142. limit=limit, marker=marker)
  143. def create(self, firewall, direction, protocol=None, from_port=None,
  144. to_port=None, cidr=None, src_dest_fw=None):
  145. if protocol and from_port and to_port:
  146. return self._create_rule(direction, protocol, from_port,
  147. to_port, cidr)
  148. elif src_dest_fw:
  149. result = None
  150. fw = (self._provider.security.vm_firewalls.get(src_dest_fw)
  151. if isinstance(src_dest_fw, str) else src_dest_fw)
  152. for rule in fw.rules:
  153. result = self._create_rule(
  154. rule.direction, rule.protocol, rule.from_port,
  155. rule.to_port, rule.cidr)
  156. return result
  157. else:
  158. return None
  159. def _create_rule(self, firewall, direction, protocol,
  160. from_port, to_port, cidr):
  161. # If cidr is None, default values is set as 0.0.0.0/0
  162. if not cidr:
  163. cidr = '0.0.0.0/0'
  164. count = len(firewall._vm_firewall.security_rules) + 1
  165. rule_name = "cb-rule-" + str(count)
  166. priority = 1000 + count
  167. destination_port_range = str(from_port) + "-" + str(to_port)
  168. source_port_range = '*'
  169. destination_address_prefix = "*"
  170. access = "Allow"
  171. direction = ("Inbound" if direction == TrafficDirection.INBOUND
  172. else "Outbound")
  173. parameters = {"priority": priority,
  174. "protocol": protocol,
  175. "source_port_range": source_port_range,
  176. "source_address_prefix": cidr,
  177. "destination_port_range": destination_port_range,
  178. "destination_address_prefix": destination_address_prefix,
  179. "access": access,
  180. "direction": direction}
  181. result = self._provider.azure_client. \
  182. create_vm_firewall_rule(firewall.id,
  183. rule_name, parameters)
  184. # pylint:disable=protected-access
  185. firewall._vm_firewall.security_rules.append(result)
  186. return AzureVMFirewallRule(firewall, result)
  187. def delete(self, firewall, rule):
  188. vm_firewall = firewall.name
  189. self.provider.azure_client. \
  190. delete_vm_firewall_rule(rule.id, vm_firewall)
  191. for i, o in enumerate(firewall._vm_firewall.security_rules):
  192. if o.id == rule.id:
  193. del firewall._vm_firewall.security_rules[i]
  194. break
  195. class AzureKeyPairService(BaseKeyPairService):
  196. PARTITION_KEY = '00000000-0000-0000-0000-000000000000'
  197. def __init__(self, provider):
  198. super(AzureKeyPairService, self).__init__(provider)
  199. @dispatch(event="provider.security.key_pairs.get",
  200. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  201. def get(self, key_pair_id):
  202. try:
  203. key_pair = self.provider.azure_client.\
  204. get_public_key(key_pair_id)
  205. if key_pair:
  206. return AzureKeyPair(self.provider, key_pair)
  207. return None
  208. except AzureException as error:
  209. log.debug("KeyPair %s was not found.", key_pair_id)
  210. log.debug(error)
  211. return None
  212. @dispatch(event="provider.security.key_pairs.list",
  213. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  214. def list(self, limit=None, marker=None):
  215. key_pairs, resume_marker = self.provider.azure_client.list_public_keys(
  216. AzureKeyPairService.PARTITION_KEY, marker=marker,
  217. limit=limit or self.provider.config.default_result_limit)
  218. results = [AzureKeyPair(self.provider, key_pair)
  219. for key_pair in key_pairs]
  220. return ServerPagedResultList(is_truncated=resume_marker,
  221. marker=resume_marker,
  222. supports_total=False,
  223. data=results)
  224. @dispatch(event="provider.security.key_pairs.find",
  225. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  226. def find(self, **kwargs):
  227. obj_list = self
  228. filters = ['name']
  229. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  230. # All kwargs should have been popped at this time.
  231. if len(kwargs) > 0:
  232. raise InvalidParamException(
  233. "Unrecognised parameters for search: %s. Supported "
  234. "attributes: %s" % (kwargs, ", ".join(filters)))
  235. return ClientPagedResultList(self.provider,
  236. matches if matches else [])
  237. @dispatch(event="provider.security.key_pairs.create",
  238. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  239. def create(self, name, public_key_material=None):
  240. AzureKeyPair.assert_valid_resource_name(name)
  241. key_pair = self.get(name)
  242. if key_pair:
  243. raise DuplicateResourceException(
  244. 'Keypair already exists with name {0}'.format(name))
  245. private_key = None
  246. if not public_key_material:
  247. public_key_material, private_key = cb_helpers.generate_key_pair()
  248. entity = {
  249. 'PartitionKey': AzureKeyPairService.PARTITION_KEY,
  250. 'RowKey': str(uuid.uuid4()),
  251. 'Name': name,
  252. 'Key': public_key_material
  253. }
  254. self.provider.azure_client.create_public_key(entity)
  255. key_pair = self.get(name)
  256. key_pair.material = private_key
  257. return key_pair
  258. @dispatch(event="provider.security.key_pairs.delete",
  259. priority=BaseKeyPairService.STANDARD_EVENT_PRIORITY)
  260. def delete(self, kp):
  261. key_pair = kp if isinstance(kp, AzureKeyPair) else self.get(kp)
  262. if key_pair:
  263. # pylint:disable=protected-access
  264. self.provider.azure_client.delete_public_key(key_pair._key_pair)
  265. class AzureStorageService(BaseStorageService):
  266. def __init__(self, provider):
  267. super(AzureStorageService, self).__init__(provider)
  268. # Initialize provider services
  269. self._volume_svc = AzureVolumeService(self.provider)
  270. self._snapshot_svc = AzureSnapshotService(self.provider)
  271. self._bucket_svc = AzureBucketService(self.provider)
  272. self._bucket_obj_svc = AzureBucketObjectService(self.provider)
  273. @property
  274. def volumes(self):
  275. return self._volume_svc
  276. @property
  277. def snapshots(self):
  278. return self._snapshot_svc
  279. @property
  280. def buckets(self):
  281. return self._bucket_svc
  282. @property
  283. def _bucket_objects(self):
  284. return self._bucket_obj_svc
  285. class AzureVolumeService(BaseVolumeService):
  286. def __init__(self, provider):
  287. super(AzureVolumeService, self).__init__(provider)
  288. @dispatch(event="provider.storage.volumes.get",
  289. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  290. def get(self, volume_id):
  291. try:
  292. volume = self.provider.azure_client.get_disk(volume_id)
  293. return AzureVolume(self.provider, volume)
  294. except (CloudError, InvalidValueException) as cloud_error:
  295. # Azure raises the cloud error if the resource not available
  296. log.exception(cloud_error)
  297. return None
  298. @dispatch(event="provider.storage.volumes.find",
  299. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  300. def find(self, **kwargs):
  301. obj_list = self
  302. filters = ['label']
  303. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  304. # All kwargs should have been popped at this time.
  305. if len(kwargs) > 0:
  306. raise InvalidParamException(
  307. "Unrecognised parameters for search: %s. Supported "
  308. "attributes: %s" % (kwargs, ", ".join(filters)))
  309. return ClientPagedResultList(self.provider,
  310. matches if matches else [])
  311. @dispatch(event="provider.storage.volumes.list",
  312. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  313. def list(self, limit=None, marker=None):
  314. azure_vols = self.provider.azure_client.list_disks()
  315. cb_vols = [AzureVolume(self.provider, vol) for vol in azure_vols]
  316. return ClientPagedResultList(self.provider, cb_vols,
  317. limit=limit, marker=marker)
  318. @dispatch(event="provider.storage.volumes.create",
  319. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  320. def create(self, label, size, zone, snapshot=None, description=None):
  321. AzureVolume.assert_valid_resource_label(label)
  322. disk_name = AzureVolume._generate_name_from_label(label, "cb-vol")
  323. tags = {'Label': label}
  324. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  325. snapshot = (self.provider.storage.snapshots.get(snapshot)
  326. if snapshot and isinstance(snapshot, str) else snapshot)
  327. if description:
  328. tags.update(Description=description)
  329. if snapshot:
  330. params = {
  331. 'location':
  332. zone_id or self.provider.azure_client.region_name,
  333. 'creation_data': {
  334. 'create_option': DiskCreateOption.copy,
  335. 'source_uri': snapshot.resource_id
  336. },
  337. 'tags': tags
  338. }
  339. disk = self.provider.azure_client.create_snapshot_disk(disk_name,
  340. params)
  341. else:
  342. params = {
  343. 'location':
  344. zone_id or self.provider.region_name,
  345. 'disk_size_gb': size,
  346. 'creation_data': {
  347. 'create_option': DiskCreateOption.empty
  348. },
  349. 'tags': tags
  350. }
  351. disk = self.provider.azure_client.create_empty_disk(disk_name,
  352. params)
  353. azure_vol = self.provider.azure_client.get_disk(disk.id)
  354. cb_vol = AzureVolume(self.provider, azure_vol)
  355. return cb_vol
  356. @dispatch(event="provider.storage.volumes.delete",
  357. priority=BaseVolumeService.STANDARD_EVENT_PRIORITY)
  358. def delete(self, volume_id):
  359. vol_id = (volume_id.id if isinstance(volume_id, AzureVolume)
  360. else volume_id)
  361. self.provider.azure_client.delete_disk(vol_id)
  362. class AzureSnapshotService(BaseSnapshotService):
  363. def __init__(self, provider):
  364. super(AzureSnapshotService, self).__init__(provider)
  365. @dispatch(event="provider.storage.snapshots.get",
  366. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  367. def get(self, snapshot_id):
  368. try:
  369. snapshot = self.provider.azure_client.get_snapshot(snapshot_id)
  370. return AzureSnapshot(self.provider, snapshot)
  371. except (CloudError, InvalidValueException) as cloud_error:
  372. # Azure raises the cloud error if the resource not available
  373. log.exception(cloud_error)
  374. return None
  375. @dispatch(event="provider.storage.snapshots.find",
  376. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  377. def find(self, **kwargs):
  378. obj_list = self
  379. filters = ['label']
  380. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  381. # All kwargs should have been popped at this time.
  382. if len(kwargs) > 0:
  383. raise InvalidParamException(
  384. "Unrecognised parameters for search: %s. Supported "
  385. "attributes: %s" % (kwargs, ", ".join(filters)))
  386. return ClientPagedResultList(self.provider,
  387. matches if matches else [])
  388. @dispatch(event="provider.storage.snapshots.list",
  389. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  390. def list(self, limit=None, marker=None):
  391. snaps = [AzureSnapshot(self.provider, obj)
  392. for obj in
  393. self.provider.azure_client.list_snapshots()]
  394. return ClientPagedResultList(self.provider, snaps, limit, marker)
  395. @dispatch(event="provider.storage.snapshots.create",
  396. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  397. def create(self, label, volume, description=None):
  398. AzureSnapshot.assert_valid_resource_label(label)
  399. snapshot_name = AzureSnapshot._generate_name_from_label(label,
  400. "cb-snap")
  401. tags = {'Label': label}
  402. if description:
  403. tags.update(Description=description)
  404. volume = (self.provider.storage.volumes.get(volume)
  405. if isinstance(volume, str) else volume)
  406. params = {
  407. 'location': self.provider.azure_client.region_name,
  408. 'creation_data': {
  409. 'create_option': DiskCreateOption.copy,
  410. 'source_uri': volume.resource_id
  411. },
  412. 'disk_size_gb': volume.size,
  413. 'tags': tags
  414. }
  415. azure_snap = self.provider.azure_client.create_snapshot(snapshot_name,
  416. params)
  417. return AzureSnapshot(self.provider, azure_snap)
  418. @dispatch(event="provider.storage.snapshots.delete",
  419. priority=BaseSnapshotService.STANDARD_EVENT_PRIORITY)
  420. def delete(self, snapshot_id):
  421. snap_id = (snapshot_id.id if isinstance(snapshot_id, AzureSnapshot)
  422. else snapshot_id)
  423. self.provider.azure_client.delete_snapshot(snap_id)
  424. class AzureBucketService(BaseBucketService):
  425. def __init__(self, provider):
  426. super(AzureBucketService, self).__init__(provider)
  427. @dispatch(event="provider.storage.buckets.get",
  428. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  429. def get(self, bucket_id):
  430. """
  431. Returns a bucket given its ID. Returns ``None`` if the bucket
  432. does not exist.
  433. """
  434. try:
  435. bucket = self.provider.azure_client.get_container(bucket_id)
  436. return AzureBucket(self.provider, bucket)
  437. except AzureException as error:
  438. log.exception(error)
  439. return None
  440. @dispatch(event="provider.storage.buckets.list",
  441. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  442. def list(self, limit=None, marker=None):
  443. buckets = [AzureBucket(self.provider, bucket)
  444. for bucket
  445. in self.provider.azure_client.list_containers()[0]]
  446. return ClientPagedResultList(self.provider, buckets,
  447. limit=limit, marker=marker)
  448. @dispatch(event="provider.storage.buckets.create",
  449. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  450. def create(self, name, location=None):
  451. """
  452. Create a new bucket.
  453. """
  454. AzureBucket.assert_valid_resource_name(name)
  455. bucket = self.provider.azure_client.create_container(name)
  456. return AzureBucket(self.provider, bucket)
  457. @dispatch(event="provider.storage.buckets.delete",
  458. priority=BaseBucketService.STANDARD_EVENT_PRIORITY)
  459. def delete(self, bucket):
  460. """
  461. Delete this bucket.
  462. """
  463. b_id = bucket.id if isinstance(bucket, AzureBucket) else bucket
  464. self.provider.azure_client.delete_container(b_id)
  465. class AzureBucketObjectService(BaseBucketObjectService):
  466. def __init__(self, provider):
  467. super(AzureBucketObjectService, self).__init__(provider)
  468. def get(self, bucket, object_id):
  469. """
  470. Retrieve a given object from this bucket.
  471. """
  472. try:
  473. obj = self.provider.azure_client.get_blob(bucket.name,
  474. object_id)
  475. return AzureBucketObject(self.provider, bucket, obj)
  476. except AzureException as azureEx:
  477. log.exception(azureEx)
  478. return None
  479. def list(self, bucket, limit=None, marker=None, prefix=None):
  480. """
  481. List all objects within this bucket.
  482. :rtype: BucketObject
  483. :return: List of all available BucketObjects within this bucket.
  484. """
  485. objects = [AzureBucketObject(self.provider, bucket, obj)
  486. for obj in
  487. self.provider.azure_client.list_blobs(
  488. bucket.name, prefix=prefix)]
  489. return ClientPagedResultList(self.provider, objects,
  490. limit=limit, marker=marker)
  491. def find(self, bucket, **kwargs):
  492. obj_list = [AzureBucketObject(self.provider, bucket, obj)
  493. for obj in
  494. self.provider.azure_client.list_blobs(bucket.name)]
  495. filters = ['name']
  496. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  497. return ClientPagedResultList(self.provider, list(matches))
  498. def create(self, bucket, name):
  499. self.provider.azure_client.create_blob_from_text(
  500. bucket.name, name, '')
  501. return self.get(bucket, name)
  502. class AzureComputeService(BaseComputeService):
  503. def __init__(self, provider):
  504. super(AzureComputeService, self).__init__(provider)
  505. self._vm_type_svc = AzureVMTypeService(self.provider)
  506. self._instance_svc = AzureInstanceService(self.provider)
  507. self._region_svc = AzureRegionService(self.provider)
  508. self._images_svc = AzureImageService(self.provider)
  509. @property
  510. def images(self):
  511. return self._images_svc
  512. @property
  513. def vm_types(self):
  514. return self._vm_type_svc
  515. @property
  516. def instances(self):
  517. return self._instance_svc
  518. @property
  519. def regions(self):
  520. return self._region_svc
  521. class AzureImageService(BaseImageService):
  522. def __init__(self, provider):
  523. super(AzureImageService, self).__init__(provider)
  524. def get(self, image_id):
  525. """
  526. Returns an Image given its id
  527. """
  528. try:
  529. image = self.provider.azure_client.get_image(image_id)
  530. return AzureMachineImage(self.provider, image)
  531. except (CloudError, InvalidValueException) as cloud_error:
  532. # Azure raises the cloud error if the resource not available
  533. log.exception(cloud_error)
  534. return None
  535. def find(self, **kwargs):
  536. obj_list = self
  537. filters = ['label']
  538. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  539. # All kwargs should have been popped at this time.
  540. if len(kwargs) > 0:
  541. raise InvalidParamException(
  542. "Unrecognised parameters for search: %s. Supported "
  543. "attributes: %s" % (kwargs, ", ".join(filters)))
  544. return ClientPagedResultList(self.provider,
  545. matches if matches else [])
  546. def list(self, filter_by_owner=True, limit=None, marker=None):
  547. """
  548. List all images.
  549. """
  550. azure_images = self.provider.azure_client.list_images()
  551. azure_gallery_refs = self.provider.azure_client.list_gallery_refs() \
  552. if not filter_by_owner else []
  553. cb_images = [AzureMachineImage(self.provider, img)
  554. for img in azure_images + azure_gallery_refs]
  555. return ClientPagedResultList(self.provider, cb_images,
  556. limit=limit, marker=marker)
  557. class AzureInstanceService(BaseInstanceService):
  558. def __init__(self, provider):
  559. super(AzureInstanceService, self).__init__(provider)
  560. def _resolve_launch_options(self, inst_name, subnet=None, zone_id=None,
  561. vm_firewalls=None):
  562. if subnet:
  563. # subnet's zone takes precedence
  564. zone_id = subnet.zone.id
  565. vm_firewall_id = None
  566. if isinstance(vm_firewalls, list) and len(vm_firewalls) > 0:
  567. if isinstance(vm_firewalls[0], VMFirewall):
  568. vm_firewalls_ids = [fw.id for fw in vm_firewalls]
  569. vm_firewall_id = vm_firewalls[0].resource_id
  570. else:
  571. vm_firewalls_ids = vm_firewalls
  572. vm_firewall = self.provider.security.\
  573. vm_firewalls.get(vm_firewalls[0])
  574. vm_firewall_id = vm_firewall.resource_id
  575. if len(vm_firewalls) > 1:
  576. new_fw = self.provider.security.vm_firewalls.\
  577. create(label='{0}-fw'.format(inst_name),
  578. description='Merge vm firewall {0}'.
  579. format(','.join(vm_firewalls_ids)))
  580. for fw in vm_firewalls:
  581. new_fw.add_rule(src_dest_fw=fw)
  582. vm_firewall_id = new_fw.resource_id
  583. return subnet.resource_id, zone_id, vm_firewall_id
  584. def _create_storage_profile(self, image, launch_config, instance_name,
  585. zone_id):
  586. if image.is_gallery_image:
  587. reference = image._image.as_dict()
  588. image_ref = {
  589. 'publisher': reference['publisher'],
  590. 'offer': reference['offer'],
  591. 'sku': reference['sku'],
  592. 'version': reference['version']
  593. }
  594. else:
  595. image_ref = {
  596. 'id': image.resource_id
  597. }
  598. storage_profile = {
  599. 'image_reference': image_ref,
  600. "os_disk": {
  601. "name": instance_name + '_os_disk',
  602. "create_option": DiskCreateOption.from_image
  603. },
  604. }
  605. if launch_config:
  606. data_disks, root_disk_size = self._process_block_device_mappings(
  607. launch_config, instance_name, zone_id)
  608. if data_disks:
  609. storage_profile['data_disks'] = data_disks
  610. if root_disk_size:
  611. storage_profile['os_disk']['disk_size_gb'] = root_disk_size
  612. return storage_profile
  613. def _process_block_device_mappings(self, launch_config,
  614. vm_name, zone=None):
  615. """
  616. Processes block device mapping information
  617. and returns a Data disk dictionary list. If new volumes
  618. are requested (source is None and destination is VOLUME), they will be
  619. created and the relevant volume ids included in the mapping.
  620. """
  621. data_disks = []
  622. root_disk_size = None
  623. def append_disk(disk_def, device_no, delete_on_terminate):
  624. # In azure, there is no option to specify terminate disks
  625. # (similar to AWS delete_on_terminate) on VM delete.
  626. # This method uses the azure tags functionality to store
  627. # the delete_on_terminate option when the virtual machine
  628. # is deleted, we parse the tags and delete accordingly
  629. disk_def['lun'] = device_no
  630. disk_def['tags'] = {
  631. 'delete_on_terminate': delete_on_terminate
  632. }
  633. data_disks.append(disk_def)
  634. for device_no, device in enumerate(launch_config.block_devices):
  635. if device.is_volume:
  636. if device.is_root:
  637. root_disk_size = device.size
  638. else:
  639. # In azure, os disk automatically created,
  640. # we are ignoring the root disk, if specified
  641. if isinstance(device.source, Snapshot):
  642. snapshot_vol = device.source.create_volume()
  643. disk_def = {
  644. # pylint:disable=protected-access
  645. 'name': snapshot_vol._volume.name,
  646. 'create_option': DiskCreateOption.attach,
  647. 'managed_disk': {
  648. 'id': snapshot_vol.id
  649. }
  650. }
  651. elif isinstance(device.source, Volume):
  652. disk_def = {
  653. # pylint:disable=protected-access
  654. 'name': device.source._volume.name,
  655. 'create_option': DiskCreateOption.attach,
  656. 'managed_disk': {
  657. 'id': device.source.id
  658. }
  659. }
  660. elif isinstance(device.source, MachineImage):
  661. disk_def = {
  662. # pylint:disable=protected-access
  663. 'name': device.source._volume.name,
  664. 'create_option': DiskCreateOption.from_image,
  665. 'source_resource_id': device.source.id
  666. }
  667. else:
  668. disk_def = {
  669. # pylint:disable=protected-access
  670. 'create_option': DiskCreateOption.empty,
  671. 'disk_size_gb': device.size
  672. }
  673. append_disk(disk_def, device_no,
  674. device.delete_on_terminate)
  675. else: # device is ephemeral
  676. # in azure we cannot add the ephemeral disks explicitly
  677. pass
  678. return data_disks, root_disk_size
  679. def create_launch_config(self):
  680. return AzureLaunchConfig(self.provider)
  681. @dispatch(event="provider.compute.instances.create",
  682. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  683. def create(self, label, image, vm_type, subnet, zone,
  684. key_pair=None, vm_firewalls=None, user_data=None,
  685. launch_config=None, **kwargs):
  686. AzureInstance.assert_valid_resource_label(label)
  687. instance_name = AzureInstance._generate_name_from_label(label,
  688. "cb-ins")
  689. image = (image if isinstance(image, AzureMachineImage) else
  690. self.provider.compute.images.get(image))
  691. if not isinstance(image, AzureMachineImage):
  692. raise Exception("Provided image %s is not a valid azure image"
  693. % image)
  694. instance_size = vm_type.id if \
  695. isinstance(vm_type, VMType) else vm_type
  696. if not subnet:
  697. # Azure has only a single zone per region; use the current one
  698. zone = self.provider.compute.regions.get(
  699. self.provider.region_name).zones[0]
  700. subnet = self.provider.networking.subnets.get_or_create_default(
  701. zone)
  702. else:
  703. subnet = (self.provider.networking.subnets.get(subnet)
  704. if isinstance(subnet, str) else subnet)
  705. zone_id = zone.id if isinstance(zone, PlacementZone) else zone
  706. subnet_id, zone_id, vm_firewall_id = \
  707. self._resolve_launch_options(instance_name,
  708. subnet, zone_id, vm_firewalls)
  709. storage_profile = self._create_storage_profile(image, launch_config,
  710. instance_name, zone_id)
  711. nic_params = {
  712. 'location': self.provider.region_name,
  713. 'ip_configurations': [{
  714. 'name': instance_name + '_ip_config',
  715. 'private_ip_allocation_method': 'Dynamic',
  716. 'subnet': {
  717. 'id': subnet_id
  718. }
  719. }]
  720. }
  721. if vm_firewall_id:
  722. nic_params['network_security_group'] = {
  723. 'id': vm_firewall_id
  724. }
  725. nic_info = self.provider.azure_client.create_nic(
  726. instance_name + '_nic',
  727. nic_params
  728. )
  729. # #! indicates shell script
  730. ud = '#cloud-config\n' + user_data \
  731. if user_data and not user_data.startswith('#!')\
  732. and not user_data.startswith('#cloud-config') else user_data
  733. # Key_pair is mandatory in azure and it should not be None.
  734. temp_key_pair = None
  735. if key_pair:
  736. key_pair = (key_pair if isinstance(key_pair, AzureKeyPair)
  737. else self.provider.security.key_pairs.get(key_pair))
  738. else:
  739. # Create a temporary keypair if none is provided to keep Azure
  740. # happy, but the private key will be discarded, so it'll be all
  741. # but useless. However, this will allow an instance to be launched
  742. # without specifying a keypair, so users may still be able to login
  743. # if they have a preinstalled keypair/password baked into the image
  744. temp_kp_name = "".join(["cb-default-kp-",
  745. str(uuid.uuid5(uuid.NAMESPACE_OID,
  746. instance_name))[-6:]])
  747. key_pair = self.provider.security.key_pairs.create(
  748. name=temp_kp_name)
  749. temp_key_pair = key_pair
  750. params = {
  751. 'location': zone_id or self.provider.region_name,
  752. 'os_profile': {
  753. 'admin_username': self.provider.vm_default_user_name,
  754. 'computer_name': instance_name,
  755. 'linux_configuration': {
  756. "disable_password_authentication": True,
  757. "ssh": {
  758. "public_keys": [{
  759. "path":
  760. "/home/{}/.ssh/authorized_keys".format(
  761. self.provider.vm_default_user_name),
  762. "key_data": key_pair._key_pair.Key
  763. }]
  764. }
  765. }
  766. },
  767. 'hardware_profile': {
  768. 'vm_size': instance_size
  769. },
  770. 'network_profile': {
  771. 'network_interfaces': [{
  772. 'id': nic_info.id
  773. }]
  774. },
  775. 'storage_profile': storage_profile,
  776. 'tags': {'Label': label}
  777. }
  778. for disk_def in storage_profile.get('data_disks', []):
  779. params['tags'] = dict(disk_def.get('tags', {}), **params['tags'])
  780. if user_data:
  781. custom_data = base64.b64encode(bytes(ud, 'utf-8'))
  782. params['os_profile']['custom_data'] = str(custom_data, 'utf-8')
  783. if not temp_key_pair:
  784. params['tags'].update(Key_Pair=key_pair.id)
  785. try:
  786. vm = self.provider.azure_client.create_vm(instance_name, params)
  787. except Exception as e:
  788. # If VM creation fails, attempt to clean up intermediary resources
  789. self.provider.azure_client.delete_nic(nic_info.id)
  790. for disk_def in storage_profile.get('data_disks', []):
  791. if disk_def.get('tags', {}).get('delete_on_terminate'):
  792. disk_id = disk_def.get('managed_disk', {}).get('id')
  793. if disk_id:
  794. vol = self.provider.storage.volumes.get(disk_id)
  795. vol.delete()
  796. raise e
  797. finally:
  798. if temp_key_pair:
  799. temp_key_pair.delete()
  800. return AzureInstance(self.provider, vm)
  801. @dispatch(event="provider.compute.instances.list",
  802. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  803. def list(self, limit=None, marker=None):
  804. """
  805. List all instances.
  806. """
  807. instances = [AzureInstance(self.provider, inst)
  808. for inst in self.provider.azure_client.list_vm()]
  809. return ClientPagedResultList(self.provider, instances,
  810. limit=limit, marker=marker)
  811. @dispatch(event="provider.compute.instances.get",
  812. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  813. def get(self, instance_id):
  814. """
  815. Returns an instance given its id. Returns None
  816. if the object does not exist.
  817. """
  818. try:
  819. vm = self.provider.azure_client.get_vm(instance_id)
  820. return AzureInstance(self.provider, vm)
  821. except (CloudError, InvalidValueException) as cloud_error:
  822. # Azure raises the cloud error if the resource not available
  823. log.exception(cloud_error)
  824. return None
  825. @dispatch(event="provider.compute.instances.find",
  826. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  827. def find(self, **kwargs):
  828. obj_list = self
  829. filters = ['label']
  830. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  831. # All kwargs should have been popped at this time.
  832. if len(kwargs) > 0:
  833. raise InvalidParamException(
  834. "Unrecognised parameters for search: %s. Supported "
  835. "attributes: %s" % (kwargs, ", ".join(filters)))
  836. return ClientPagedResultList(self.provider,
  837. matches if matches else [])
  838. @dispatch(event="provider.compute.instances.delete",
  839. priority=BaseInstanceService.STANDARD_EVENT_PRIORITY)
  840. def delete(self, inst):
  841. """
  842. Permanently terminate this instance.
  843. After deleting the VM. we are deleting the network interface
  844. associated to the instance, and also removing OS disk and data disks
  845. where tag with name 'delete_on_terminate' has value True.
  846. """
  847. ins = inst if isinstance(inst, AzureInstance) else self.get(inst)
  848. if not inst:
  849. return
  850. # Remove IPs first to avoid a network interface conflict
  851. for public_ip_id in ins._public_ip_ids:
  852. ins.remove_floating_ip(public_ip_id)
  853. self.provider.azure_client.deallocate_vm(ins.id)
  854. self.provider.azure_client.delete_vm(ins.id)
  855. for nic_id in ins._nic_ids:
  856. self.provider.azure_client.delete_nic(nic_id)
  857. for data_disk in ins._vm.storage_profile.data_disks:
  858. if data_disk.managed_disk:
  859. if ins._vm.tags.get('delete_on_terminate',
  860. 'False') == 'True':
  861. self.provider.azure_client. \
  862. delete_disk(data_disk.managed_disk.id)
  863. if ins._vm.storage_profile.os_disk.managed_disk:
  864. self.provider.azure_client. \
  865. delete_disk(ins._vm.storage_profile.os_disk.managed_disk.id)
  866. class AzureVMTypeService(BaseVMTypeService):
  867. def __init__(self, provider):
  868. super(AzureVMTypeService, self).__init__(provider)
  869. @property
  870. def instance_data(self):
  871. """
  872. Fetch info about the available instances.
  873. """
  874. r = self.provider.azure_client.list_vm_types()
  875. return r
  876. @dispatch(event="provider.compute.vm_types.list",
  877. priority=BaseVMTypeService.STANDARD_EVENT_PRIORITY)
  878. def list(self, limit=None, marker=None):
  879. vm_types = [AzureVMType(self.provider, vm_type)
  880. for vm_type in self.instance_data]
  881. return ClientPagedResultList(self.provider, vm_types,
  882. limit=limit, marker=marker)
  883. class AzureRegionService(BaseRegionService):
  884. def __init__(self, provider):
  885. super(AzureRegionService, self).__init__(provider)
  886. @dispatch(event="provider.compute.regions.get",
  887. priority=BaseRegionService.STANDARD_EVENT_PRIORITY)
  888. def get(self, region_id):
  889. region = None
  890. for azureRegion in self.provider.azure_client.list_locations():
  891. if azureRegion.name == region_id:
  892. region = AzureRegion(self.provider, azureRegion)
  893. break
  894. return region
  895. @dispatch(event="provider.compute.regions.list",
  896. priority=BaseRegionService.STANDARD_EVENT_PRIORITY)
  897. def list(self, limit=None, marker=None):
  898. regions = [AzureRegion(self.provider, region)
  899. for region in self.provider.azure_client.list_locations()]
  900. return ClientPagedResultList(self.provider, regions,
  901. limit=limit, marker=marker)
  902. @property
  903. def current(self):
  904. return self.get(self.provider.region_name)
  905. class AzureNetworkingService(BaseNetworkingService):
  906. def __init__(self, provider):
  907. super(AzureNetworkingService, self).__init__(provider)
  908. self._network_service = AzureNetworkService(self.provider)
  909. self._subnet_service = AzureSubnetService(self.provider)
  910. self._router_service = AzureRouterService(self.provider)
  911. self._gateway_service = AzureGatewayService(self.provider)
  912. self._floating_ip_service = AzureFloatingIPService(self.provider)
  913. class AzureNetworkService(BaseNetworkService):
  914. def __init__(self, provider):
  915. super(AzureNetworkService, self).__init__(provider)
  916. @dispatch(event="provider.networking.networks.get",
  917. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  918. def get(self, network_id):
  919. try:
  920. network = self.provider.azure_client.get_network(network_id)
  921. return AzureNetwork(self.provider, network)
  922. except (CloudError, InvalidValueException) as cloud_error:
  923. # Azure raises the cloud error if the resource not available
  924. log.exception(cloud_error)
  925. return None
  926. @dispatch(event="provider.networking.networks.list",
  927. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  928. def list(self, limit=None, marker=None):
  929. networks = [AzureNetwork(self.provider, network)
  930. for network in self.provider.azure_client.list_networks()]
  931. return ClientPagedResultList(self.provider, networks,
  932. limit=limit, marker=marker)
  933. @dispatch(event="provider.networking.networks.create",
  934. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  935. def create(self, label, cidr_block):
  936. AzureNetwork.assert_valid_resource_label(label)
  937. params = {
  938. 'location': self.provider.azure_client.region_name,
  939. 'address_space': {
  940. 'address_prefixes': [cidr_block]
  941. },
  942. 'tags': {'Label': label}
  943. }
  944. network_name = AzureNetwork._generate_name_from_label(label, 'cb-net')
  945. az_network = self.provider.azure_client.create_network(network_name,
  946. params)
  947. cb_network = AzureNetwork(self.provider, az_network)
  948. return cb_network
  949. @dispatch(event="provider.networking.networks.delete",
  950. priority=BaseNetworkService.STANDARD_EVENT_PRIORITY)
  951. def delete(self, net):
  952. net_id = net.id if isinstance(net, AzureNetwork) else net
  953. if net_id:
  954. self.provider.azure_client.delete_network(net_id)
  955. class AzureSubnetService(BaseSubnetService):
  956. def __init__(self, provider):
  957. super(AzureSubnetService, self).__init__(provider)
  958. def _list_subnets(self, network=None):
  959. result_list = []
  960. if network:
  961. network_id = network.id \
  962. if isinstance(network, Network) else network
  963. result_list = self.provider.azure_client.list_subnets(network_id)
  964. else:
  965. for net in self.provider.networking.networks:
  966. try:
  967. result_list.extend(self.provider.azure_client.list_subnets(
  968. net.id
  969. ))
  970. except CloudError as cloud_error:
  971. if "NotFound" in cloud_error.error.error:
  972. log.exception(cloud_error)
  973. else:
  974. raise cloud_error
  975. subnets = [AzureSubnet(self.provider, subnet)
  976. for subnet in result_list]
  977. return subnets
  978. @dispatch(event="provider.networking.subnets.get",
  979. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  980. def get(self, subnet_id):
  981. """
  982. Azure does not provide an api to get the subnet directly by id.
  983. It also requires the network id.
  984. To make it consistent across the providers the following code
  985. gets the specific code from the subnet list.
  986. """
  987. try:
  988. azure_subnet = self.provider.azure_client.get_subnet(subnet_id)
  989. return AzureSubnet(self.provider,
  990. azure_subnet) if azure_subnet else None
  991. except (CloudError, InvalidValueException) as cloud_error:
  992. # Azure raises the cloud error if the resource not available
  993. log.exception(cloud_error)
  994. return None
  995. @dispatch(event="provider.networking.subnets.list",
  996. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  997. def list(self, network=None, limit=None, marker=None):
  998. return ClientPagedResultList(self.provider,
  999. self._list_subnets(network),
  1000. limit=limit, marker=marker)
  1001. @dispatch(event="provider.networking.subnets.find",
  1002. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  1003. def find(self, network=None, **kwargs):
  1004. obj_list = self._list_subnets(network)
  1005. filters = ['label']
  1006. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  1007. return ClientPagedResultList(self.provider,
  1008. matches if matches else [])
  1009. @dispatch(event="provider.networking.subnets.create",
  1010. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  1011. def create(self, label, network, cidr_block, zone):
  1012. AzureSubnet.assert_valid_resource_label(label)
  1013. # Although Subnet doesn't support tags in Azure, we use the parent
  1014. # Network's tags to track its subnets' labels
  1015. subnet_name = AzureSubnet._generate_name_from_label(label, "cb-sn")
  1016. network_id = network.id \
  1017. if isinstance(network, Network) else network
  1018. subnet_info = self.provider.azure_client\
  1019. .create_subnet(
  1020. network_id,
  1021. subnet_name,
  1022. {
  1023. 'address_prefix': cidr_block
  1024. }
  1025. )
  1026. subnet = AzureSubnet(self.provider, subnet_info)
  1027. subnet.label = label
  1028. return subnet
  1029. @dispatch(event="provider.networking.subnets.delete",
  1030. priority=BaseSubnetService.STANDARD_EVENT_PRIORITY)
  1031. def delete(self, subnet):
  1032. sn = subnet if isinstance(subnet, AzureSubnet) else self.get(subnet)
  1033. if sn:
  1034. self.provider.azure_client.delete_subnet(sn.id)
  1035. # Although Subnet doesn't support labels, we use the parent
  1036. # Network's tags to track the subnet's labels, thus that
  1037. # network-level tag must be deleted with the subnet
  1038. net_id = sn.network_id
  1039. az_network = self.provider.azure_client.get_network(net_id)
  1040. az_network.tags.pop(sn.tag_name)
  1041. self._provider.azure_client.update_network_tags(
  1042. az_network.id, az_network)
  1043. class AzureRouterService(BaseRouterService):
  1044. def __init__(self, provider):
  1045. super(AzureRouterService, self).__init__(provider)
  1046. @dispatch(event="provider.networking.routers.get",
  1047. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1048. def get(self, router_id):
  1049. try:
  1050. route = self.provider.azure_client.get_route_table(router_id)
  1051. return AzureRouter(self.provider, route)
  1052. except (CloudError, InvalidValueException) as cloud_error:
  1053. # Azure raises the cloud error if the resource not available
  1054. log.exception(cloud_error)
  1055. return None
  1056. @dispatch(event="provider.networking.routers.find",
  1057. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1058. def find(self, **kwargs):
  1059. obj_list = self
  1060. filters = ['label']
  1061. matches = cb_helpers.generic_find(filters, kwargs, obj_list)
  1062. # All kwargs should have been popped at this time.
  1063. if len(kwargs) > 0:
  1064. raise InvalidParamException(
  1065. "Unrecognised parameters for search: %s. Supported "
  1066. "attributes: %s" % (kwargs, ", ".join(filters)))
  1067. return ClientPagedResultList(self.provider,
  1068. matches if matches else [])
  1069. @dispatch(event="provider.networking.routers.list",
  1070. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1071. def list(self, limit=None, marker=None):
  1072. routes = [AzureRouter(self.provider, route)
  1073. for route in
  1074. self.provider.azure_client.list_route_tables()]
  1075. return ClientPagedResultList(self.provider,
  1076. routes,
  1077. limit=limit, marker=marker)
  1078. @dispatch(event="provider.networking.routers.create",
  1079. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1080. def create(self, label, network):
  1081. router_name = AzureRouter._generate_name_from_label(label, "cb-router")
  1082. parameters = {"location": self.provider.region_name,
  1083. "tags": {'Label': label}}
  1084. route = self.provider.azure_client. \
  1085. create_route_table(router_name, parameters)
  1086. return AzureRouter(self.provider, route)
  1087. @dispatch(event="provider.networking.routers.delete",
  1088. priority=BaseRouterService.STANDARD_EVENT_PRIORITY)
  1089. def delete(self, router):
  1090. r = router if isinstance(router, AzureRouter) else self.get(router)
  1091. if r:
  1092. self.provider.azure_client.delete_route_table(r.name)
  1093. class AzureGatewayService(BaseGatewayService):
  1094. def __init__(self, provider):
  1095. super(AzureGatewayService, self).__init__(provider)
  1096. # Azure doesn't have a notion of a route table or an internet
  1097. # gateway as OS and AWS so create placeholder objects of the
  1098. # AzureInternetGateway here.
  1099. # http://bit.ly/2BqGdVh
  1100. # Singleton returned by the list and get methods
  1101. def _gateway_singleton(self, network):
  1102. return AzureInternetGateway(self._provider, None, network)
  1103. def get_or_create_inet_gateway(self, network):
  1104. return self._gateway_singleton(network)
  1105. def list(self, network, limit=None, marker=None):
  1106. gws = [self._gateway_singleton(network)]
  1107. return ClientPagedResultList(self._provider,
  1108. gws,
  1109. limit=limit, marker=marker)
  1110. def delete(self, network, gateway):
  1111. pass
  1112. class AzureFloatingIPService(BaseFloatingIPService):
  1113. def __init__(self, provider):
  1114. super(AzureFloatingIPService, self).__init__(provider)
  1115. def get(self, gateway, fip_id):
  1116. log.debug("Getting Azure Floating IP container with the id: %s",
  1117. fip_id)
  1118. fip = [fip for fip in self if fip.id == fip_id]
  1119. return fip[0] if fip else None
  1120. def list(self, gateway, limit=None, marker=None):
  1121. floating_ips = [AzureFloatingIP(self.provider, floating_ip,
  1122. gateway.network_id)
  1123. for floating_ip in self.provider.azure_client.
  1124. list_floating_ips()]
  1125. return ClientPagedResultList(self.provider, floating_ips,
  1126. limit=limit, marker=marker)
  1127. def create(self, gateway):
  1128. public_ip_parameters = {
  1129. 'location': self.provider.azure_client.region_name,
  1130. 'public_ip_allocation_method': 'Static'
  1131. }
  1132. public_ip_name = AzureFloatingIP._generate_name_from_label('cb-fip-')
  1133. floating_ip = self.provider.azure_client.\
  1134. create_floating_ip(public_ip_name, public_ip_parameters)
  1135. return AzureFloatingIP(self.provider, floating_ip, gateway.network_id)
  1136. def delete(self, gateway, fip):
  1137. fip_id = fip if isinstance(fip, AzureFloatingIP) else fip
  1138. self._provider.azure_client.delete_floating_ip(fip_id)