resources.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  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, BaseMachineImage, \
  9. BaseSecurityGroup, BaseSecurityGroupRule, BaseSnapshot, BaseVolume, \
  10. ClientPagedResultList
  11. from cloudbridge.cloud.interfaces import VolumeState
  12. from cloudbridge.cloud.interfaces.resources import Instance, \
  13. MachineImageState, SnapshotState
  14. from cloudbridge.cloud.providers.azure import helpers as azure_helpers
  15. from msrestazure.azure_exceptions import CloudError
  16. NETWORK_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  17. '{resourceGroupName}/providers/Microsoft.Network/' \
  18. 'virtualNetworks/{virtualNetworkName}'
  19. IMAGE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  20. '{resourceGroupName}/providers/Microsoft.Compute/' \
  21. 'images/{imageName}'
  22. INSTANCE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  23. '{resourceGroupName}/providers/Microsoft.Compute/' \
  24. 'virtualMachines/{vmName}'
  25. VOLUME_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  26. '{resourceGroupName}/providers/Microsoft.Compute/' \
  27. 'disks/{diskName}'
  28. SNAPSHOT_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  29. '{resourceGroupName}/providers/Microsoft.Compute/' \
  30. 'snapshots/{snapshotName}'
  31. IMAGE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  32. '{resourceGroupName}/providers/Microsoft.Compute/' \
  33. 'images/{imageName}'
  34. NETWORK_SECURITY_GROUP_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  35. 'resourceGroups/{resourceGroupName}/' \
  36. 'providers/Microsoft.Network/' \
  37. 'networkSecurityGroups/' \
  38. '{networkSecurityGroupName}'
  39. NETWORK_SECURITY_RULE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  40. 'resourceGroups/{resourceGroupName}/' \
  41. 'providers/Microsoft.Network/' \
  42. 'networkSecurityGroups/' \
  43. '{networkSecurityGroupName}' \
  44. '/securityRules/{securityRuleName}'
  45. SUBNET_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  46. '{resourceGroupName}/providers/Microsoft.Network' \
  47. '/virtualNetworks/{virtualNetworkName}/subnets' \
  48. '/{subnetName}'
  49. PUBLIC_IP_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  50. '/{resourceGroupName}/providers/Microsoft.Network' \
  51. '/publicIPAddresses/{publicIpAddressName}'
  52. ROUTE_TABLE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  53. '/{resourceGroupName}/providers/Microsoft.Network' \
  54. '/routeTables/{routeTableName}'
  55. ROUTE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  56. '{resourceGroupName}/providers/Microsoft.Network' \
  57. '/routeTables/{routeTableName}/routes/{routeName}'
  58. NETWORK_INTERFACE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  59. 'resourceGroups/{resourceGroupName}' \
  60. '/providers/Microsoft.Network/' \
  61. 'networkInterfaces/{networkInterfaceName}'
  62. RESOURCE_GROUP_NAME = 'resourceGroupName'
  63. SUBSCRIPTION_ID = 'subscriptionId'
  64. NETWORK_NAME = 'virtualNetworkName'
  65. IMAGE_NAME = 'imageName'
  66. VM_NAME = 'vmName'
  67. VOLUME_NAME = 'diskName'
  68. SNAPSHOT_NAME = 'snapshotName'
  69. SECURITY_GROUP_NAME = 'networkSecurityGroupName'
  70. SECURITY_GROUP_RULE_NAME = 'securityRuleName'
  71. SUBNET_NAME = 'subnetName'
  72. PUBLIC_IP_NAME = 'publicIpAddressName'
  73. ROUTE_TABLE_NAME = 'routeTableName'
  74. ROUTE_NAME = 'routeName'
  75. NETWORK_INTERFACE_NAME = 'networkInterfaceName'
  76. class AzureSecurityGroup(BaseSecurityGroup):
  77. def __init__(self, provider, security_group):
  78. super(AzureSecurityGroup, self).__init__(provider, security_group)
  79. self._security_group = security_group
  80. if not self._security_group.tags:
  81. self._security_group.tags = {}
  82. @property
  83. def network_id(self):
  84. return self._security_group.resource_guid
  85. @property
  86. def resource_name(self):
  87. return self._security_group.name
  88. @property
  89. def name(self):
  90. return self._security_group.tags.get('Name', self._security_group.name)
  91. @name.setter
  92. def name(self, value):
  93. self._security_group.tags.update(Name=value)
  94. self._provider.azure_client. \
  95. update_security_group_tags(self.resource_name,
  96. self._security_group.tags)
  97. @property
  98. def description(self):
  99. return self._security_group.tags.get('Description', None)
  100. @description.setter
  101. def description(self, value):
  102. self._security_group.tags.update(Description=value)
  103. self._provider.azure_client.\
  104. update_security_group_tags(self.resource_name,
  105. self._security_group.tags)
  106. @property
  107. def rules(self):
  108. security_group_rules = []
  109. for custom_rule in self._security_group.security_rules:
  110. sg_custom_rule = AzureSecurityGroupRule(self._provider,
  111. custom_rule, self)
  112. security_group_rules.append(sg_custom_rule)
  113. return security_group_rules
  114. def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
  115. cidr_ip=None, src_group=None):
  116. """
  117. Create a security group rule.
  118. You need to pass in either ``src_group`` OR ``ip_protocol``,
  119. ``from_port``, ``to_port``, and ``cidr_ip``. In other words, either
  120. you are authorizing another group or you are authorizing some
  121. ip-based rule.
  122. :type ip_protocol: str
  123. :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
  124. :type from_port: int
  125. :param from_port: The beginning port number you are enabling
  126. :type to_port: int
  127. :param to_port: The ending port number you are enabling
  128. :type cidr_ip: str or list of strings
  129. :param cidr_ip: The CIDR block you are providing access to.
  130. :type src_group: ``object`` of :class:`.SecurityGroup`
  131. :param src_group: The Security Group you are granting access to.
  132. :rtype: :class:``.SecurityGroupRule``
  133. :return: Rule object if successful or ``None``.
  134. """
  135. if not cidr_ip:
  136. cidr_ip = '0.0.0.0/0'
  137. rule = self.get_rule(ip_protocol, from_port,
  138. to_port, cidr_ip, src_group)
  139. if not rule:
  140. # resource_group = self._provider.resource_group
  141. count = len(self.rules) + 1
  142. rule_name = "Rule - " + str(count)
  143. priority = count * 100
  144. destination_port_range = "*"
  145. destination_address_prefix = "*"
  146. access = "Allow"
  147. direction = "Inbound"
  148. parameters = {"protocol": ip_protocol,
  149. "source_port_range":
  150. str(from_port) + "-" + str(to_port),
  151. "destination_port_range": destination_port_range,
  152. "priority": priority,
  153. "source_address_prefix": cidr_ip,
  154. "destination_address_prefix":
  155. destination_address_prefix,
  156. "access": access, "direction": direction}
  157. result = self._provider.azure_client. \
  158. create_security_group_rule(self.resource_name,
  159. rule_name, parameters)
  160. self._security_group.security_rules.append(result)
  161. return AzureSecurityGroupRule(self._provider, result, self)
  162. return rule
  163. def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
  164. cidr_ip=None, src_group=None):
  165. for rule in self.rules:
  166. if (rule.ip_protocol == ip_protocol and
  167. rule.from_port == str(from_port) and
  168. rule.to_port == str(to_port) and
  169. rule.cidr_ip == cidr_ip):
  170. return rule
  171. return None
  172. def to_json(self):
  173. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  174. js = {k: v for (k, v) in attr if not k.startswith('_')}
  175. json_rules = [r.to_json() for r in self.rules]
  176. js['rules'] = [json.loads(r) for r in json_rules]
  177. if js.get('network_id'):
  178. js.pop('network_id') # Omit for consistency across cloud providers
  179. return json.dumps(js, sort_keys=True)
  180. class AzureSecurityGroupRule(BaseSecurityGroupRule):
  181. def __init__(self, provider, rule, parent):
  182. super(AzureSecurityGroupRule, self).__init__(provider, rule, parent)
  183. @property
  184. def name(self):
  185. return self._rule.name
  186. @property
  187. def id(self):
  188. return self._rule.id
  189. @property
  190. def ip_protocol(self):
  191. return self._rule.protocol
  192. @property
  193. def from_port(self):
  194. if self._rule.source_port_range == '*':
  195. return self._rule.source_port_range
  196. source_port_range = self._rule.source_port_range
  197. port_range_split = source_port_range.split('-', 1)
  198. return port_range_split[0]
  199. @property
  200. def to_port(self):
  201. if self._rule.source_port_range == '*':
  202. return self._rule.source_port_range
  203. source_port_range = self._rule.source_port_range
  204. port_range_split = source_port_range.split('-', 1)
  205. return port_range_split[1]
  206. @property
  207. def cidr_ip(self):
  208. return self._rule.source_address_prefix
  209. @property
  210. def group(self):
  211. return self.parent
  212. def to_json(self):
  213. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  214. js = {k: v for (k, v) in attr if not k.startswith('_')}
  215. js['group'] = self.group.id if self.group else ''
  216. js['parent'] = self.parent.id if self.parent else ''
  217. return json.dumps(js, sort_keys=True)
  218. def delete(self):
  219. security_group = self.parent.name
  220. self._provider.azure_client. \
  221. delete_security_group_rule(self.name, security_group)
  222. for i, o in enumerate(self.parent._security_group.security_rules):
  223. if o.name == self.name:
  224. del self.parent._security_group.security_rules[i]
  225. break
  226. class AzureBucketObject(BaseBucketObject):
  227. def __init__(self, provider, container, key):
  228. super(AzureBucketObject, self).__init__(provider)
  229. self._container = container
  230. self._key = key
  231. @property
  232. def id(self):
  233. return self._key.name
  234. @property
  235. def name(self):
  236. """
  237. Get this object's name.
  238. """
  239. return self._key.name
  240. @property
  241. def size(self):
  242. """
  243. Get this object's size.
  244. """
  245. return self._key.properties.content_length
  246. @property
  247. def last_modified(self):
  248. """
  249. Get the date and time this object was last modified.
  250. """
  251. return self._key.properties.last_modified. \
  252. strftime("%Y-%m-%dT%H:%M:%S.%f")
  253. def iter_content(self):
  254. """
  255. Returns this object's content as an
  256. iterable.
  257. """
  258. content_stream = self._provider.azure_client. \
  259. get_blob_content(self._container.name, self._key.name)
  260. if content_stream:
  261. content_stream.seek(0)
  262. return content_stream
  263. def upload(self, data):
  264. """
  265. Set the contents of this object to the data read from the source
  266. string.
  267. """
  268. try:
  269. self._provider.azure_client.create_blob_from_text(
  270. self._container.name, self.name, data)
  271. return True
  272. except AzureException:
  273. return False
  274. def upload_from_file(self, path):
  275. """
  276. Store the contents of the file pointed by the "path" variable.
  277. """
  278. try:
  279. self._provider.azure_client.create_blob_from_file(
  280. self._container.name, self.name, path)
  281. return True
  282. except AzureException:
  283. return False
  284. def delete(self):
  285. """
  286. Delete this object.
  287. :rtype: bool
  288. :return: True if successful
  289. """
  290. try:
  291. self._provider.azure_client.delete_blob(
  292. self._container.name, self.name)
  293. return True
  294. except AzureException:
  295. return False
  296. def generate_url(self, expires_in=0):
  297. """
  298. Generate a URL to this object.
  299. """
  300. return self._provider.azure_client.get_blob_url(
  301. self._container.name, self.name)
  302. class AzureBucket(BaseBucket):
  303. def __init__(self, provider, bucket):
  304. super(AzureBucket, self).__init__(provider)
  305. self._bucket = bucket
  306. @property
  307. def id(self):
  308. return self._bucket.name
  309. @property
  310. def name(self):
  311. """
  312. Get this bucket's name.
  313. """
  314. return self._bucket.name
  315. def get(self, key):
  316. """
  317. Retrieve a given object from this bucket.
  318. """
  319. try:
  320. obj = self._provider.azure_client.get_blob(self.name, key)
  321. return AzureBucketObject(self._provider, self, obj)
  322. except AzureException:
  323. return None
  324. def list(self, limit=None, marker=None, prefix=None):
  325. """
  326. List all objects within this bucket.
  327. :rtype: BucketObject
  328. :return: List of all available BucketObjects within this bucket.
  329. """
  330. objects = [AzureBucketObject(self._provider, self, obj)
  331. for obj in
  332. self._provider.azure_client.list_blobs(
  333. self.name, prefix=prefix)]
  334. return ClientPagedResultList(self._provider, objects,
  335. limit=limit, marker=marker)
  336. def delete(self, delete_contents=True):
  337. """
  338. Delete this bucket.
  339. """
  340. try:
  341. self._provider.azure_client.delete_container(self.name)
  342. return True
  343. except AzureException:
  344. return False
  345. def create_object(self, name):
  346. self._provider.azure_client.create_blob_from_text(
  347. self.name, name, '')
  348. return self.get(name)
  349. def exists(self, name):
  350. """
  351. Determine if an object with given name exists in this bucket.
  352. """
  353. return True if self.get(name) else False
  354. class AzureVolume(BaseVolume):
  355. VOLUME_STATE_MAP = {
  356. 'InProgress': VolumeState.CREATING,
  357. 'Creating': VolumeState.CREATING,
  358. 'Unattached': VolumeState.AVAILABLE,
  359. 'Attached': VolumeState.IN_USE,
  360. 'Deleting': VolumeState.CONFIGURING,
  361. 'Updating': VolumeState.CONFIGURING,
  362. 'Deleted': VolumeState.DELETED,
  363. 'Failed': VolumeState.ERROR
  364. }
  365. def __init__(self, provider, volume):
  366. super(AzureVolume, self).__init__(provider)
  367. self._volume = volume
  368. self._url_params = azure_helpers.\
  369. parse_url(VOLUME_RESOURCE_ID, volume.id)
  370. self._description = None
  371. self._status = 'unknown'
  372. self.update_status()
  373. if not self._volume.tags:
  374. self._volume.tags = {}
  375. def update_status(self):
  376. if not self._volume.provisioning_state == 'Succeeded':
  377. self._status = self._volume.provisioning_state
  378. elif self._volume.owner_id:
  379. self._status = 'Attached'
  380. else:
  381. self._status = 'Unattached'
  382. @property
  383. def id(self):
  384. return self._volume.id
  385. @property
  386. def resource_name(self):
  387. return self._volume.name
  388. @property
  389. def name(self):
  390. """
  391. Get the volume name.
  392. .. note:: an instance must have a (case sensitive) tag ``Name``
  393. """
  394. return self._volume.tags.get('Name', self._volume.name)
  395. @name.setter
  396. # pylint:disable=arguments-differ
  397. def name(self, value):
  398. """
  399. Set the volume name.
  400. """
  401. # self._volume.name = value
  402. self._volume.tags.update(Name=value)
  403. self._provider.azure_client.\
  404. update_disk_tags(self.resource_name,
  405. self._volume.tags)
  406. @property
  407. def description(self):
  408. return self._volume.tags.get('Description', None)
  409. @description.setter
  410. def description(self, value):
  411. self._volume.tags.update(Description=value)
  412. self._provider.azure_client.\
  413. update_disk_tags(self.resource_name,
  414. self._volume.tags)
  415. @property
  416. def size(self):
  417. return self._volume.disk_size_gb
  418. @property
  419. def create_time(self):
  420. return self._volume.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  421. @property
  422. def zone_id(self):
  423. return self._volume.location
  424. @property
  425. def source(self):
  426. if self._volume.creation_data.source_uri:
  427. return self._provider.block_store.snapshots. \
  428. get(self._volume.creation_data.source_uri)
  429. return None
  430. @property
  431. def attachments(self):
  432. if self._volume.owner_id:
  433. return BaseAttachmentInfo(self,
  434. self._volume.owner_id,
  435. None)
  436. else:
  437. return None
  438. def attach(self, instance, device=None):
  439. """
  440. Attach this volume to an instance.
  441. """
  442. try:
  443. instance_id = instance.id if isinstance(
  444. instance,
  445. Instance) else instance
  446. params = azure_helpers.parse_url(INSTANCE_RESOURCE_ID,
  447. instance_id)
  448. vm = self._provider.azure_client.get_vm(params.get(VM_NAME))
  449. vm.storage_profile.data_disks.append({
  450. 'lun': len(vm.storage_profile.data_disks),
  451. 'name': self.resource_name,
  452. 'create_option': 'attach',
  453. 'managed_disk': {
  454. 'id': self.id
  455. }
  456. })
  457. self._provider.azure_client\
  458. .create_or_update_vm(params.get(VM_NAME), vm)
  459. return True
  460. except CloudError:
  461. return False
  462. def detach(self, force=False):
  463. """
  464. Detach this volume from an instance.
  465. """
  466. virtual_machine = None
  467. for index, vm in enumerate(
  468. self._provider.azure_client.list_vm()):
  469. for item in vm.storage_profile.data_disks:
  470. if item.managed_disk and item.managed_disk.id == self.id:
  471. vm.storage_profile.data_disks.remove(item)
  472. virtual_machine = vm
  473. break
  474. if virtual_machine:
  475. break
  476. if virtual_machine:
  477. self._provider.azure_client.create_or_update_vm(
  478. virtual_machine.name,
  479. virtual_machine
  480. )
  481. return True
  482. return False
  483. def create_snapshot(self, name, description=None):
  484. """
  485. Create a snapshot of this Volume.
  486. """
  487. return self._provider.block_store.snapshots.create(name, self.id)
  488. def delete(self):
  489. """
  490. Delete this volume.
  491. """
  492. try:
  493. self._provider.azure_client.\
  494. delete_disk(self.resource_name)
  495. return True
  496. except CloudError:
  497. return False
  498. @property
  499. def state(self):
  500. return AzureVolume.VOLUME_STATE_MAP.get(
  501. self._status, VolumeState.UNKNOWN)
  502. def refresh(self):
  503. """
  504. Refreshes the state of this volume by re-querying the cloud provider
  505. for its latest state.
  506. """
  507. try:
  508. self._volume = self._provider.azure_client.\
  509. get_disk(self.resource_name)
  510. self.update_status()
  511. except (CloudError, ValueError):
  512. # The volume no longer exists and cannot be refreshed.
  513. # set the status to unknown
  514. self._status = 'unknown'
  515. class AzureSnapshot(BaseSnapshot):
  516. SNAPSHOT_STATE_MAP = {
  517. 'InProgress': SnapshotState.PENDING,
  518. 'Succeeded': SnapshotState.AVAILABLE,
  519. 'Failed': SnapshotState.ERROR,
  520. 'Updating': SnapshotState.CONFIGURING,
  521. 'Deleting': SnapshotState.CONFIGURING,
  522. 'Deleted': SnapshotState.UNKNOWN
  523. }
  524. def __init__(self, provider, snapshot):
  525. super(AzureSnapshot, self).__init__(provider)
  526. self._snapshot = snapshot
  527. self._description = None
  528. self._url_params = azure_helpers.\
  529. parse_url(SNAPSHOT_RESOURCE_ID, snapshot.id)
  530. self._status = self._snapshot.provisioning_state
  531. if not self._snapshot.tags:
  532. self._snapshot.tags = {}
  533. @property
  534. def id(self):
  535. return self._snapshot.id
  536. @property
  537. def resource_name(self):
  538. return self._snapshot.name
  539. @property
  540. def name(self):
  541. """
  542. Get the snapshot name.
  543. .. note:: an instance must have a (case sensitive) tag ``Name``
  544. """
  545. return self._snapshot.tags.get('Name', self._snapshot.name)
  546. @name.setter
  547. # pylint:disable=arguments-differ
  548. def name(self, value):
  549. """
  550. Set the snapshot name.
  551. """
  552. # self._snapshot.name = value
  553. self._snapshot.tags.update(Name=value)
  554. self._provider.azure_client. \
  555. update_snapshot_tags(self.resource_name,
  556. self._snapshot.tags)
  557. @property
  558. def description(self):
  559. return self._snapshot.tags.get('Description', None)
  560. @description.setter
  561. def description(self, value):
  562. self._snapshot.tags.update(Description=value)
  563. self._provider.azure_client.\
  564. update_snapshot_tags(self.resource_name,
  565. self._snapshot.tags)
  566. @property
  567. def size(self):
  568. return self._snapshot.disk_size_gb
  569. @property
  570. def volume_id(self):
  571. return self._snapshot.creation_data.source_uri
  572. @property
  573. def create_time(self):
  574. return self._snapshot.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  575. @property
  576. def state(self):
  577. return AzureSnapshot.SNAPSHOT_STATE_MAP.get(
  578. self._status, SnapshotState.UNKNOWN)
  579. def refresh(self):
  580. """
  581. Refreshes the state of this snapshot by re-querying the cloud provider
  582. for its latest state.
  583. """
  584. try:
  585. self._snapshot = self._provider.azure_client.\
  586. get_snapshot(self.resource_name)
  587. self._status = self._snapshot.provisioning_state
  588. except (CloudError, ValueError):
  589. # The snapshot no longer exists and cannot be refreshed.
  590. # set the status to unknown
  591. self._status = 'unknown'
  592. def delete(self):
  593. """
  594. Delete this snapshot.
  595. """
  596. try:
  597. self._provider.azure_client.delete_snapshot(self.resource_name)
  598. return True
  599. except CloudError:
  600. return False
  601. def create_volume(self, placement=None,
  602. size=None, volume_type=None, iops=None):
  603. """
  604. Create a new Volume from this Snapshot.
  605. """
  606. return self._provider.block_store.volumes.\
  607. create(self.resource_name, self.size,
  608. zone=placement, snapshot=self)
  609. class AzureMachineImage(BaseMachineImage):
  610. IMAGE_STATE_MAP = {
  611. 'pending': MachineImageState.PENDING,
  612. 'available': MachineImageState.AVAILABLE,
  613. 'failed': MachineImageState.ERROR
  614. }
  615. def __init__(self, provider, image):
  616. super(AzureMachineImage, self).__init__(provider)
  617. self._image = image
  618. self._description = None
  619. self._status = self._image.provisioning_state
  620. if not self._image.tags:
  621. self._image.tags = {}
  622. @property
  623. def id(self):
  624. """
  625. Get the image identifier.
  626. :rtype: ``str``
  627. :return: ID for this instance as returned by the cloud middleware.
  628. """
  629. return self._image.id
  630. @property
  631. def resource_name(self):
  632. return self._image.name
  633. @property
  634. def name(self):
  635. """
  636. Get the snapshot name.
  637. .. note:: an instance must have a (case sensitive) tag ``Name``
  638. """
  639. return self._image.tags.get('Name', self._image.name)
  640. @name.setter
  641. # pylint:disable=arguments-differ
  642. def name(self, value):
  643. """
  644. Set the snapshot name.
  645. """
  646. # self._snapshot.name = value
  647. self._image.tags.update(Name=value)
  648. self._provider.azure_client. \
  649. update_snapshot_tags(self.resource_name,
  650. self._image.tags)
  651. @property
  652. def description(self):
  653. return self._image.tags.get('Description', None)
  654. # @property
  655. # def min_disk(self):
  656. # """
  657. # Returns the minimum size of the disk that's required to
  658. # boot this image (in GB)
  659. #
  660. # :rtype: ``int``
  661. # :return: The minimum disk size needed by this image
  662. # """
  663. # bdm = self._ec2_image.block_device_mapping
  664. # return bdm[self._ec2_image.root_device_name].size
  665. #
  666. # def delete(self):
  667. # """
  668. # Delete this image
  669. # """
  670. # self._ec2_image.deregister(delete_snapshot=True)
  671. #
  672. # @property
  673. # def state(self):
  674. # return AzureMachineImage.IMAGE_STATE_MAP.get(
  675. # self._ec2_image.state, MachineImageState.UNKNOWN)
  676. #
  677. # def refresh(self):
  678. # """
  679. # Refreshes the state of this instance by re-querying
  680. # the cloud provider
  681. # for its latest state.
  682. # """
  683. # image = self._provider.compute.images.get(self.id)
  684. # if image:
  685. # # pylint:disable=protected-access
  686. # self._ec2_image = image._ec2_image
  687. # else:
  688. # # image no longer exists
  689. # self._ec2_image.state = "unknown"