resources.py 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. """
  2. DataTypes used by this provider
  3. """
  4. import inspect
  5. import json
  6. from azure.common import AzureException
  7. from cloudbridge.cloud.base.resources import BaseAttachmentInfo, \
  8. BaseBucket, BaseBucketObject, BaseInstanceType,\
  9. BaseMachineImage, BaseNetwork, \
  10. BasePlacementZone, BaseRegion, BaseSecurityGroup, BaseSecurityGroupRule, \
  11. BaseSnapshot, BaseSubnet, BaseVolume, ClientPagedResultList
  12. from cloudbridge.cloud.interfaces import VolumeState
  13. from cloudbridge.cloud.interfaces.resources import Instance, \
  14. MachineImageState, NetworkState, SnapshotState
  15. from cloudbridge.cloud.providers.azure import helpers as azure_helpers
  16. from msrestazure.azure_exceptions import CloudError
  17. NETWORK_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  18. '{resourceGroupName}/providers/Microsoft.Network/' \
  19. 'virtualNetworks/{virtualNetworkName}'
  20. IMAGE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  21. '{resourceGroupName}/providers/Microsoft.Compute/' \
  22. 'images/{imageName}'
  23. INSTANCE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  24. '{resourceGroupName}/providers/Microsoft.Compute/' \
  25. 'virtualMachines/{vmName}'
  26. VOLUME_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  27. '{resourceGroupName}/providers/Microsoft.Compute/' \
  28. 'disks/{diskName}'
  29. SNAPSHOT_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  30. '{resourceGroupName}/providers/Microsoft.Compute/' \
  31. 'snapshots/{snapshotName}'
  32. NETWORK_SECURITY_GROUP_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  33. 'resourceGroups/{resourceGroupName}/' \
  34. 'providers/Microsoft.Network/' \
  35. 'networkSecurityGroups/' \
  36. '{networkSecurityGroupName}'
  37. NETWORK_SECURITY_RULE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  38. 'resourceGroups/{resourceGroupName}/' \
  39. 'providers/Microsoft.Network/' \
  40. 'networkSecurityGroups/' \
  41. '{networkSecurityGroupName}' \
  42. '/securityRules/{securityRuleName}'
  43. SUBNET_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  44. '{resourceGroupName}/providers/Microsoft.Network' \
  45. '/virtualNetworks/{virtualNetworkName}/subnets' \
  46. '/{subnetName}'
  47. PUBLIC_IP_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  48. '/{resourceGroupName}/providers/Microsoft.Network' \
  49. '/publicIPAddresses/{publicIpAddressName}'
  50. ROUTE_TABLE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  51. '/{resourceGroupName}/providers/Microsoft.Network' \
  52. '/routeTables/{routeTableName}'
  53. ROUTE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  54. '{resourceGroupName}/providers/Microsoft.Network' \
  55. '/routeTables/{routeTableName}/routes/{routeName}'
  56. NETWORK_INTERFACE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  57. 'resourceGroups/{resourceGroupName}' \
  58. '/providers/Microsoft.Network/' \
  59. 'networkInterfaces/{networkInterfaceName}'
  60. RESOURCE_GROUP_NAME = 'resourceGroupName'
  61. SUBSCRIPTION_ID = 'subscriptionId'
  62. NETWORK_NAME = 'virtualNetworkName'
  63. IMAGE_NAME = 'imageName'
  64. VM_NAME = 'vmName'
  65. VOLUME_NAME = 'diskName'
  66. SNAPSHOT_NAME = 'snapshotName'
  67. SECURITY_GROUP_NAME = 'networkSecurityGroupName'
  68. SECURITY_GROUP_RULE_NAME = 'securityRuleName'
  69. SUBNET_NAME = 'subnetName'
  70. PUBLIC_IP_NAME = 'publicIpAddressName'
  71. ROUTE_TABLE_NAME = 'routeTableName'
  72. ROUTE_NAME = 'routeName'
  73. NETWORK_INTERFACE_NAME = 'networkInterfaceName'
  74. class AzureSecurityGroup(BaseSecurityGroup):
  75. def __init__(self, provider, security_group):
  76. super(AzureSecurityGroup, self).__init__(provider, security_group)
  77. self._security_group = security_group
  78. if not self._security_group.tags:
  79. self._security_group.tags = {}
  80. @property
  81. def network_id(self):
  82. return self._security_group.resource_guid
  83. @property
  84. def resource_name(self):
  85. return self._security_group.name
  86. @property
  87. def name(self):
  88. return self._security_group.tags.get('Name', self._security_group.name)
  89. @name.setter
  90. def name(self, value):
  91. self._security_group.tags.update(Name=value)
  92. self._provider.azure_client. \
  93. update_security_group_tags(self.resource_name,
  94. self._security_group.tags)
  95. @property
  96. def description(self):
  97. return self._security_group.tags.get('Description', None)
  98. @description.setter
  99. def description(self, value):
  100. self._security_group.tags.update(Description=value)
  101. self._provider.azure_client. \
  102. update_security_group_tags(self.resource_name,
  103. self._security_group.tags)
  104. @property
  105. def rules(self):
  106. security_group_rules = []
  107. for custom_rule in self._security_group.security_rules:
  108. sg_custom_rule = AzureSecurityGroupRule(self._provider,
  109. custom_rule, self)
  110. security_group_rules.append(sg_custom_rule)
  111. return security_group_rules
  112. def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
  113. cidr_ip=None, src_group=None):
  114. """
  115. Create a security group rule.
  116. You need to pass in either ``src_group`` OR ``ip_protocol``,
  117. ``from_port``, ``to_port``, and ``cidr_ip``. In other words, either
  118. you are authorizing another group or you are authorizing some
  119. ip-based rule.
  120. :type ip_protocol: str
  121. :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
  122. :type from_port: int
  123. :param from_port: The beginning port number you are enabling
  124. :type to_port: int
  125. :param to_port: The ending port number you are enabling
  126. :type cidr_ip: str or list of strings
  127. :param cidr_ip: The CIDR block you are providing access to.
  128. :type src_group: ``object`` of :class:`.SecurityGroup`
  129. :param src_group: The Security Group you are granting access to.
  130. :rtype: :class:``.SecurityGroupRule``
  131. :return: Rule object if successful or ``None``.
  132. """
  133. if not cidr_ip:
  134. cidr_ip = '0.0.0.0/0'
  135. rule = self.get_rule(ip_protocol, from_port,
  136. to_port, cidr_ip, src_group)
  137. if not rule:
  138. # resource_group = self._provider.resource_group
  139. count = len(self.rules) + 1
  140. rule_name = "Rule - " + str(count)
  141. priority = count * 100
  142. destination_port_range = "*"
  143. destination_address_prefix = "*"
  144. access = "Allow"
  145. direction = "Inbound"
  146. parameters = {"protocol": ip_protocol,
  147. "source_port_range":
  148. str(from_port) + "-" + str(to_port),
  149. "destination_port_range": destination_port_range,
  150. "priority": priority,
  151. "source_address_prefix": cidr_ip,
  152. "destination_address_prefix":
  153. destination_address_prefix,
  154. "access": access, "direction": direction}
  155. result = self._provider.azure_client. \
  156. create_security_group_rule(self.resource_name,
  157. rule_name, parameters)
  158. self._security_group.security_rules.append(result)
  159. return AzureSecurityGroupRule(self._provider, result, self)
  160. return rule
  161. def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
  162. cidr_ip=None, src_group=None):
  163. for rule in self.rules:
  164. if (rule.ip_protocol == ip_protocol and
  165. rule.from_port == str(from_port) and
  166. rule.to_port == str(to_port) and
  167. rule.cidr_ip == cidr_ip):
  168. return rule
  169. return None
  170. def to_json(self):
  171. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  172. js = {k: v for (k, v) in attr if not k.startswith('_')}
  173. json_rules = [r.to_json() for r in self.rules]
  174. js['rules'] = [json.loads(r) for r in json_rules]
  175. if js.get('network_id'):
  176. js.pop('network_id') # Omit for consistency across cloud providers
  177. return json.dumps(js, sort_keys=True)
  178. class AzureSecurityGroupRule(BaseSecurityGroupRule):
  179. def __init__(self, provider, rule, parent):
  180. super(AzureSecurityGroupRule, self).__init__(provider, rule, parent)
  181. @property
  182. def name(self):
  183. return self._rule.name
  184. @property
  185. def id(self):
  186. return self._rule.id
  187. @property
  188. def ip_protocol(self):
  189. return self._rule.protocol
  190. @property
  191. def from_port(self):
  192. if self._rule.source_port_range == '*':
  193. return self._rule.source_port_range
  194. source_port_range = self._rule.source_port_range
  195. port_range_split = source_port_range.split('-', 1)
  196. return port_range_split[0]
  197. @property
  198. def to_port(self):
  199. if self._rule.source_port_range == '*':
  200. return self._rule.source_port_range
  201. source_port_range = self._rule.source_port_range
  202. port_range_split = source_port_range.split('-', 1)
  203. return port_range_split[1]
  204. @property
  205. def cidr_ip(self):
  206. return self._rule.source_address_prefix
  207. @property
  208. def group(self):
  209. return self.parent
  210. def to_json(self):
  211. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  212. js = {k: v for (k, v) in attr if not k.startswith('_')}
  213. js['group'] = self.group.id if self.group else ''
  214. js['parent'] = self.parent.id if self.parent else ''
  215. return json.dumps(js, sort_keys=True)
  216. def delete(self):
  217. security_group = self.parent.name
  218. self._provider.azure_client. \
  219. delete_security_group_rule(self.name, security_group)
  220. for i, o in enumerate(self.parent._security_group.security_rules):
  221. if o.name == self.name:
  222. del self.parent._security_group.security_rules[i]
  223. break
  224. class AzureBucketObject(BaseBucketObject):
  225. def __init__(self, provider, container, key):
  226. super(AzureBucketObject, self).__init__(provider)
  227. self._container = container
  228. self._key = key
  229. @property
  230. def id(self):
  231. return self._key.name
  232. @property
  233. def name(self):
  234. """
  235. Get this object's name.
  236. """
  237. return self._key.name
  238. @property
  239. def size(self):
  240. """
  241. Get this object's size.
  242. """
  243. return self._key.properties.content_length
  244. @property
  245. def last_modified(self):
  246. """
  247. Get the date and time this object was last modified.
  248. """
  249. return self._key.properties.last_modified. \
  250. strftime("%Y-%m-%dT%H:%M:%S.%f")
  251. def iter_content(self):
  252. """
  253. Returns this object's content as an
  254. iterable.
  255. """
  256. content_stream = self._provider.azure_client. \
  257. get_blob_content(self._container.name, self._key.name)
  258. if content_stream:
  259. content_stream.seek(0)
  260. return content_stream
  261. def upload(self, data):
  262. """
  263. Set the contents of this object to the data read from the source
  264. string.
  265. """
  266. try:
  267. self._provider.azure_client.create_blob_from_text(
  268. self._container.name, self.name, data)
  269. return True
  270. except AzureException:
  271. return False
  272. def upload_from_file(self, path):
  273. """
  274. Store the contents of the file pointed by the "path" variable.
  275. """
  276. try:
  277. self._provider.azure_client.create_blob_from_file(
  278. self._container.name, self.name, path)
  279. return True
  280. except AzureException:
  281. return False
  282. def delete(self):
  283. """
  284. Delete this object.
  285. :rtype: bool
  286. :return: True if successful
  287. """
  288. try:
  289. self._provider.azure_client.delete_blob(
  290. self._container.name, self.name)
  291. return True
  292. except AzureException:
  293. return False
  294. def generate_url(self, expires_in=0):
  295. """
  296. Generate a URL to this object.
  297. """
  298. return self._provider.azure_client.get_blob_url(
  299. self._container.name, self.name)
  300. class AzureBucket(BaseBucket):
  301. def __init__(self, provider, bucket):
  302. super(AzureBucket, self).__init__(provider)
  303. self._bucket = bucket
  304. @property
  305. def id(self):
  306. return self._bucket.name
  307. @property
  308. def name(self):
  309. """
  310. Get this bucket's name.
  311. """
  312. return self._bucket.name
  313. def get(self, key):
  314. """
  315. Retrieve a given object from this bucket.
  316. """
  317. try:
  318. obj = self._provider.azure_client.get_blob(self.name, key)
  319. return AzureBucketObject(self._provider, self, obj)
  320. except AzureException:
  321. return None
  322. def list(self, limit=None, marker=None, prefix=None):
  323. """
  324. List all objects within this bucket.
  325. :rtype: BucketObject
  326. :return: List of all available BucketObjects within this bucket.
  327. """
  328. objects = [AzureBucketObject(self._provider, self, obj)
  329. for obj in
  330. self._provider.azure_client.list_blobs(
  331. self.name, prefix=prefix)]
  332. return ClientPagedResultList(self._provider, objects,
  333. limit=limit, marker=marker)
  334. def delete(self, delete_contents=True):
  335. """
  336. Delete this bucket.
  337. """
  338. try:
  339. self._provider.azure_client.delete_container(self.name)
  340. return True
  341. except AzureException:
  342. return False
  343. def create_object(self, name):
  344. self._provider.azure_client.create_blob_from_text(
  345. self.name, name, '')
  346. return self.get(name)
  347. def exists(self, name):
  348. """
  349. Determine if an object with given name exists in this bucket.
  350. """
  351. return True if self.get(name) else False
  352. class AzureVolume(BaseVolume):
  353. VOLUME_STATE_MAP = {
  354. 'InProgress': VolumeState.CREATING,
  355. 'Creating': VolumeState.CREATING,
  356. 'Unattached': VolumeState.AVAILABLE,
  357. 'Attached': VolumeState.IN_USE,
  358. 'Deleting': VolumeState.CONFIGURING,
  359. 'Updating': VolumeState.CONFIGURING,
  360. 'Deleted': VolumeState.DELETED,
  361. 'Failed': VolumeState.ERROR
  362. }
  363. def __init__(self, provider, volume):
  364. super(AzureVolume, self).__init__(provider)
  365. self._volume = volume
  366. self._url_params = azure_helpers. \
  367. parse_url(VOLUME_RESOURCE_ID, volume.id)
  368. self._description = None
  369. self._status = 'unknown'
  370. self.update_status()
  371. if not self._volume.tags:
  372. self._volume.tags = {}
  373. def update_status(self):
  374. if not self._volume.provisioning_state == 'Succeeded':
  375. self._status = self._volume.provisioning_state
  376. elif self._volume.owner_id:
  377. self._status = 'Attached'
  378. else:
  379. self._status = 'Unattached'
  380. @property
  381. def id(self):
  382. return self._volume.id
  383. @property
  384. def resource_name(self):
  385. return self._volume.name
  386. @property
  387. def name(self):
  388. """
  389. Get the volume name.
  390. .. note:: an instance must have a (case sensitive) tag ``Name``
  391. """
  392. return self._volume.tags.get('Name', self._volume.name)
  393. @name.setter
  394. # pylint:disable=arguments-differ
  395. def name(self, value):
  396. """
  397. Set the volume name.
  398. """
  399. # self._volume.name = value
  400. self._volume.tags.update(Name=value)
  401. self._provider.azure_client. \
  402. update_disk_tags(self.resource_name,
  403. self._volume.tags)
  404. @property
  405. def description(self):
  406. return self._volume.tags.get('Description', None)
  407. @description.setter
  408. def description(self, value):
  409. self._volume.tags.update(Description=value)
  410. self._provider.azure_client. \
  411. update_disk_tags(self.resource_name,
  412. self._volume.tags)
  413. @property
  414. def size(self):
  415. return self._volume.disk_size_gb
  416. @property
  417. def create_time(self):
  418. return self._volume.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  419. @property
  420. def zone_id(self):
  421. return self._volume.location
  422. @property
  423. def source(self):
  424. if self._volume.creation_data.source_uri:
  425. return self._provider.block_store.snapshots. \
  426. get(self._volume.creation_data.source_uri)
  427. return None
  428. @property
  429. def attachments(self):
  430. if self._volume.owner_id:
  431. return BaseAttachmentInfo(self,
  432. self._volume.owner_id,
  433. None)
  434. else:
  435. return None
  436. def attach(self, instance, device=None):
  437. """
  438. Attach this volume to an instance.
  439. """
  440. try:
  441. instance_id = instance.id if isinstance(
  442. instance,
  443. Instance) else instance
  444. params = azure_helpers.parse_url(INSTANCE_RESOURCE_ID,
  445. instance_id)
  446. vm = self._provider.azure_client.get_vm(params.get(VM_NAME))
  447. vm.storage_profile.data_disks.append({
  448. 'lun': len(vm.storage_profile.data_disks),
  449. 'name': self.resource_name,
  450. 'create_option': 'attach',
  451. 'managed_disk': {
  452. 'id': self.id
  453. }
  454. })
  455. self._provider.azure_client \
  456. .create_or_update_vm(params.get(VM_NAME), vm)
  457. return True
  458. except CloudError:
  459. return False
  460. def detach(self, force=False):
  461. """
  462. Detach this volume from an instance.
  463. """
  464. virtual_machine = None
  465. for index, vm in enumerate(
  466. self._provider.azure_client.list_vm()):
  467. for item in vm.storage_profile.data_disks:
  468. if item.managed_disk and item.managed_disk.id == self.id:
  469. vm.storage_profile.data_disks.remove(item)
  470. virtual_machine = vm
  471. break
  472. if virtual_machine:
  473. break
  474. if virtual_machine:
  475. self._provider.azure_client.create_or_update_vm(
  476. virtual_machine.name,
  477. virtual_machine
  478. )
  479. return True
  480. return False
  481. def create_snapshot(self, name, description=None):
  482. """
  483. Create a snapshot of this Volume.
  484. """
  485. return self._provider.block_store.snapshots.create(name, self.id)
  486. def delete(self):
  487. """
  488. Delete this volume.
  489. """
  490. try:
  491. self._provider.azure_client. \
  492. delete_disk(self.resource_name)
  493. return True
  494. except CloudError:
  495. return False
  496. @property
  497. def state(self):
  498. return AzureVolume.VOLUME_STATE_MAP.get(
  499. self._status, VolumeState.UNKNOWN)
  500. def refresh(self):
  501. """
  502. Refreshes the state of this volume by re-querying the cloud provider
  503. for its latest state.
  504. """
  505. try:
  506. self._volume = self._provider.azure_client. \
  507. get_disk(self.resource_name)
  508. self.update_status()
  509. except (CloudError, ValueError):
  510. # The volume no longer exists and cannot be refreshed.
  511. # set the status to unknown
  512. self._status = 'unknown'
  513. class AzureSnapshot(BaseSnapshot):
  514. SNAPSHOT_STATE_MAP = {
  515. 'InProgress': SnapshotState.PENDING,
  516. 'Succeeded': SnapshotState.AVAILABLE,
  517. 'Failed': SnapshotState.ERROR,
  518. 'Updating': SnapshotState.CONFIGURING,
  519. 'Deleting': SnapshotState.CONFIGURING,
  520. 'Deleted': SnapshotState.UNKNOWN
  521. }
  522. def __init__(self, provider, snapshot):
  523. super(AzureSnapshot, self).__init__(provider)
  524. self._snapshot = snapshot
  525. self._description = None
  526. self._url_params = azure_helpers. \
  527. parse_url(SNAPSHOT_RESOURCE_ID, snapshot.id)
  528. self._status = self._snapshot.provisioning_state
  529. if not self._snapshot.tags:
  530. self._snapshot.tags = {}
  531. @property
  532. def id(self):
  533. return self._snapshot.id
  534. @property
  535. def resource_name(self):
  536. return self._snapshot.name
  537. @property
  538. def name(self):
  539. """
  540. Get the snapshot name.
  541. .. note:: an instance must have a (case sensitive) tag ``Name``
  542. """
  543. return self._snapshot.tags.get('Name', self._snapshot.name)
  544. @name.setter
  545. # pylint:disable=arguments-differ
  546. def name(self, value):
  547. """
  548. Set the snapshot name.
  549. """
  550. # self._snapshot.name = value
  551. self._snapshot.tags.update(Name=value)
  552. self._provider.azure_client. \
  553. update_snapshot_tags(self.resource_name,
  554. self._snapshot.tags)
  555. @property
  556. def description(self):
  557. return self._snapshot.tags.get('Description', None)
  558. @description.setter
  559. def description(self, value):
  560. self._snapshot.tags.update(Description=value)
  561. self._provider.azure_client. \
  562. update_snapshot_tags(self.resource_name,
  563. self._snapshot.tags)
  564. @property
  565. def size(self):
  566. return self._snapshot.disk_size_gb
  567. @property
  568. def volume_id(self):
  569. return self._snapshot.creation_data.source_uri
  570. @property
  571. def create_time(self):
  572. return self._snapshot.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  573. @property
  574. def state(self):
  575. return AzureSnapshot.SNAPSHOT_STATE_MAP.get(
  576. self._status, SnapshotState.UNKNOWN)
  577. def refresh(self):
  578. """
  579. Refreshes the state of this snapshot by re-querying the cloud provider
  580. for its latest state.
  581. """
  582. try:
  583. self._snapshot = self._provider.azure_client. \
  584. get_snapshot(self.resource_name)
  585. self._status = self._snapshot.provisioning_state
  586. except (CloudError, ValueError):
  587. # The snapshot no longer exists and cannot be refreshed.
  588. # set the status to unknown
  589. self._status = 'unknown'
  590. def delete(self):
  591. """
  592. Delete this snapshot.
  593. """
  594. try:
  595. self._provider.azure_client.delete_snapshot(self.resource_name)
  596. return True
  597. except CloudError:
  598. return False
  599. def create_volume(self, placement=None,
  600. size=None, volume_type=None, iops=None):
  601. """
  602. Create a new Volume from this Snapshot.
  603. """
  604. return self._provider.block_store.volumes. \
  605. create(self.resource_name, self.size,
  606. zone=placement, snapshot=self)
  607. class AzureMachineImage(BaseMachineImage):
  608. IMAGE_STATE_MAP = {
  609. 'InProgress': MachineImageState.PENDING,
  610. 'Succeeded': MachineImageState.AVAILABLE,
  611. 'Failed': MachineImageState.ERROR
  612. }
  613. def __init__(self, provider, image):
  614. super(AzureMachineImage, self).__init__(provider)
  615. self._image = image
  616. self._url_params = azure_helpers. \
  617. parse_url(IMAGE_RESOURCE_ID, image.id)
  618. self._state = self._image.provisioning_state
  619. if not self._image.tags:
  620. self._image.tags = {}
  621. @property
  622. def id(self):
  623. """
  624. Get the image identifier.
  625. :rtype: ``str``
  626. :return: ID for this instance as returned by the cloud middleware.
  627. """
  628. return self._image.id
  629. @property
  630. def name(self):
  631. """
  632. Get the image name.
  633. :rtype: ``str``
  634. :return: Name for this image as returned by the cloud middleware.
  635. """
  636. return self._image.tags.get('Name', self._image.name)
  637. @name.setter
  638. def name(self, value):
  639. """
  640. Set the image name.
  641. """
  642. self._image.tags.update(Name=value)
  643. self._provider.azure_client. \
  644. update_image_tags(self.resource_name, self._image.tags)
  645. @property
  646. def description(self):
  647. """
  648. Get the image description.
  649. :rtype: ``str``
  650. :return: Description for this image as returned by the cloud middleware
  651. """
  652. return self._image.tags.get('Description', None)
  653. @description.setter
  654. def description(self, value):
  655. """
  656. Set the image name.
  657. """
  658. self._image.tags.update(Description=value)
  659. self._provider.azure_client. \
  660. update_image_tags(self.resource_name, self._image.tags)
  661. @property
  662. def resource_name(self):
  663. return self._image.name
  664. @property
  665. def min_disk(self):
  666. """
  667. Returns the minimum size of the disk that's required to
  668. boot this image (in GB)
  669. :rtype: ``int``
  670. :return: The minimum disk size needed by this image
  671. """
  672. return self._image.storage_profile.os_disk.disk_size_gb
  673. def delete(self):
  674. """
  675. Delete this image
  676. """
  677. self._provider.azure_client.delete_image(self.resource_name)
  678. @property
  679. def state(self):
  680. return AzureMachineImage.IMAGE_STATE_MAP.get(
  681. self._state, MachineImageState.UNKNOWN)
  682. def refresh(self):
  683. """
  684. Refreshes the state of this instance by re-querying the cloud provider
  685. for its latest state.
  686. """
  687. try:
  688. self._image = self._provider.azure_client\
  689. .get_image(self.resource_name)
  690. self._state = self._image.provisioning_state
  691. except CloudError:
  692. # image no longer exists
  693. self._state = "unknown"
  694. class AzureNetwork(BaseNetwork):
  695. NETWORK_STATE_MAP = {
  696. 'InProgress': NetworkState.PENDING,
  697. 'Succeeded': NetworkState.AVAILABLE,
  698. }
  699. def __init__(self, provider, network):
  700. super(AzureNetwork, self).__init__(provider)
  701. self._network = network
  702. self._state = self._network.provisioning_state
  703. @property
  704. def id(self):
  705. return self._network.id
  706. @property
  707. def name(self):
  708. """
  709. Get the network name.
  710. .. note:: the network must have a (case sensitive) tag ``Name``
  711. """
  712. return self._network.name
  713. @name.setter
  714. # pylint:disable=arguments-differ
  715. def name(self, value):
  716. """
  717. Set the network name.
  718. """
  719. self._network.name = value
  720. @property
  721. def external(self):
  722. """
  723. For Azure, all VPC networks can be connected to the Internet so always
  724. return ``True``.
  725. """
  726. return True
  727. @property
  728. def state(self):
  729. return AzureNetwork.NETWORK_STATE_MAP.get(
  730. self._state, NetworkState.UNKNOWN)
  731. def refresh(self):
  732. """
  733. Refreshes the state of this network by re-querying the cloud provider
  734. for its latest state.
  735. """
  736. try:
  737. self._network = self._provider.azure_client.get_network(self.name)
  738. self._state = self._network.provisioning_state
  739. except (CloudError, ValueError):
  740. # The network no longer exists and cannot be refreshed.
  741. # set the status to unknown
  742. self._state = 'unknown'
  743. @property
  744. def cidr_block(self):
  745. return self._network.address_space
  746. def delete(self):
  747. """
  748. Delete an existing network.
  749. """
  750. try:
  751. network = self._provider.azure_client.delete_network(self.name)
  752. return True if network else False
  753. except CloudError:
  754. return False
  755. def subnets(self):
  756. raise NotImplementedError('AzureNetworkService '
  757. 'not implemented this property')
  758. def create_subnet(self, cidr_block, name=None, zone=None):
  759. raise NotImplementedError('AzureNetworkService '
  760. 'not implemented this property')
  761. class AzureRegion(BaseRegion):
  762. def __init__(self, provider, azure_region):
  763. super(AzureRegion, self).__init__(provider)
  764. self._azure_region = azure_region
  765. @property
  766. def id(self):
  767. return self._azure_region.id
  768. @property
  769. def name(self):
  770. return self._azure_region.name
  771. @property
  772. def zones(self):
  773. """
  774. Access information about placement zones within this region.
  775. """
  776. # As Azure does not have zones feature, mapping the region
  777. # information in the zones.
  778. return [AzurePlacementZone(self._provider,
  779. self._azure_region.id,
  780. self._azure_region.name)]
  781. class AzurePlacementZone(BasePlacementZone):
  782. """
  783. As Azure does not provide zones (limited support), we are mapping the
  784. region information in the zones.
  785. """
  786. def __init__(self, provider, zone, region):
  787. super(AzurePlacementZone, self).__init__(provider)
  788. self._azure_zone = zone
  789. self._azure_region = region
  790. @property
  791. def id(self):
  792. """
  793. Get the zone id
  794. :rtype: ``str``
  795. :return: ID for this zone as returned by the cloud middleware.
  796. """
  797. return self._azure_zone
  798. @property
  799. def name(self):
  800. """
  801. Get the zone name.
  802. :rtype: ``str``
  803. :return: Name for this zone as returned by the cloud middleware.
  804. """
  805. return self._azure_region
  806. @property
  807. def region_name(self):
  808. """
  809. Get the region that this zone belongs to.
  810. :rtype: ``str``
  811. :return: Name of this zone's region as returned by the
  812. cloud middleware
  813. """
  814. return self._azure_region
  815. class AzureSubnet(BaseSubnet):
  816. def __init__(self, provider, subnet):
  817. super(AzureSubnet, self).__init__(provider)
  818. self._subnet = subnet
  819. @property
  820. def id(self):
  821. return self._subnet.id
  822. @property
  823. def name(self):
  824. """
  825. Get the subnet name.
  826. .. note:: the subnet must have a (case sensitive) tag ``Name``
  827. """
  828. return self._subnet.name
  829. @name.setter
  830. # pylint:disable=arguments-differ
  831. def name(self, value):
  832. """
  833. Set the network name.
  834. """
  835. self._subnet.name = value
  836. @property
  837. def cidr_block(self):
  838. return self._subnet.address_prefix
  839. @property
  840. def network_id(self):
  841. params = azure_helpers.parse_url(SUBNET_RESOURCE_ID, self._subnet.id)
  842. networkname = params.get(NETWORK_NAME)
  843. network = self._provider.azure_client.get_network(networkname)
  844. if network:
  845. return network.id
  846. else:
  847. return None
  848. @property
  849. def zone(self):
  850. return self._provider.compute.regions.current.zones[0]
  851. def delete(self):
  852. return None
  853. class AzureInstanceType(BaseInstanceType):
  854. def __init__(self, provider, instance_type):
  855. super(AzureInstanceType, self).__init__(provider)
  856. self._inst_type = instance_type
  857. @property
  858. def id(self):
  859. return self._inst_type.name
  860. @property
  861. def name(self):
  862. return self._inst_type.name
  863. @property
  864. def family(self):
  865. return "Unknown"
  866. @property
  867. def vcpus(self):
  868. return self._inst_type.number_of_cores
  869. @property
  870. def ram(self):
  871. return self._inst_type.memory_in_mb
  872. @property
  873. def size_root_disk(self):
  874. return self._inst_type.os_disk_size_in_mb / 1024
  875. @property
  876. def size_ephemeral_disks(self):
  877. return self._inst_type.resource_disk_size_in_mb / 1024
  878. @property
  879. def num_ephemeral_disks(self):
  880. return 1
  881. @property
  882. def extra_data(self):
  883. return None