resources.py 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535
  1. """
  2. DataTypes used by this provider
  3. """
  4. import inspect
  5. import json
  6. import logging
  7. import time
  8. from azure.common import AzureException
  9. from azure.mgmt.network.models import NetworkSecurityGroup
  10. from cloudbridge.cloud.base.resources import BaseAttachmentInfo, \
  11. BaseBucket, BaseBucketObject, BaseFloatingIP, \
  12. BaseInstance, BaseInstanceType, \
  13. BaseLaunchConfig, BaseMachineImage, BaseNetwork, \
  14. BasePlacementZone, BaseRegion, \
  15. BaseSecurityGroup, BaseSecurityGroupRule, BaseSnapshot, BaseSubnet, \
  16. BaseVolume, ClientPagedResultList
  17. from cloudbridge.cloud.interfaces import InstanceState, VolumeState
  18. from cloudbridge.cloud.interfaces.resources import Instance, \
  19. MachineImageState, NetworkState, SnapshotState
  20. from cloudbridge.cloud.providers.azure import helpers as azure_helpers
  21. from msrestazure.azure_exceptions import CloudError
  22. import pysftp
  23. log = logging.getLogger(__name__)
  24. NETWORK_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  25. '{resourceGroupName}/providers/Microsoft.Network/' \
  26. 'virtualNetworks/{virtualNetworkName}'
  27. IMAGE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  28. '{resourceGroupName}/providers/Microsoft.Compute/' \
  29. 'images/{imageName}'
  30. INSTANCE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  31. '{resourceGroupName}/providers/Microsoft.Compute/' \
  32. 'virtualMachines/{vmName}'
  33. VOLUME_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  34. '{resourceGroupName}/providers/Microsoft.Compute/' \
  35. 'disks/{diskName}'
  36. SNAPSHOT_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  37. '{resourceGroupName}/providers/Microsoft.Compute/' \
  38. 'snapshots/{snapshotName}'
  39. NETWORK_SECURITY_GROUP_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  40. 'resourceGroups/{resourceGroupName}/' \
  41. 'providers/Microsoft.Network/' \
  42. 'networkSecurityGroups/' \
  43. '{networkSecurityGroupName}'
  44. NETWORK_SECURITY_RULE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  45. 'resourceGroups/{resourceGroupName}/' \
  46. 'providers/Microsoft.Network/' \
  47. 'networkSecurityGroups/' \
  48. '{networkSecurityGroupName}' \
  49. '/securityRules/{securityRuleName}'
  50. SUBNET_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  51. '{resourceGroupName}/providers/Microsoft.Network' \
  52. '/virtualNetworks/{virtualNetworkName}/subnets' \
  53. '/{subnetName}'
  54. PUBLIC_IP_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  55. '/{resourceGroupName}/providers/Microsoft.Network' \
  56. '/publicIPAddresses/{publicIpAddressName}'
  57. ROUTE_TABLE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups' \
  58. '/{resourceGroupName}/providers/Microsoft.Network' \
  59. '/routeTables/{routeTableName}'
  60. ROUTE_RESOURCE_ID = '/subscriptions/{subscriptionId}/resourceGroups/' \
  61. '{resourceGroupName}/providers/Microsoft.Network' \
  62. '/routeTables/{routeTableName}/routes/{routeName}'
  63. NETWORK_INTERFACE_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  64. 'resourceGroups/{resourceGroupName}' \
  65. '/providers/Microsoft.Network/' \
  66. 'networkInterfaces/{networkInterfaceName}'
  67. LOCATION_RESOURCE_ID = '/subscriptions/{subscriptionId}/' \
  68. 'locations/{locationName}'
  69. RESOURCE_GROUP_NAME = 'resourceGroupName'
  70. SUBSCRIPTION_ID = 'subscriptionId'
  71. NETWORK_NAME = 'virtualNetworkName'
  72. IMAGE_NAME = 'imageName'
  73. VM_NAME = 'vmName'
  74. VOLUME_NAME = 'diskName'
  75. SNAPSHOT_NAME = 'snapshotName'
  76. SECURITY_GROUP_NAME = 'networkSecurityGroupName'
  77. SECURITY_GROUP_RULE_NAME = 'securityRuleName'
  78. SUBNET_NAME = 'subnetName'
  79. PUBLIC_IP_NAME = 'publicIpAddressName'
  80. ROUTE_TABLE_NAME = 'routeTableName'
  81. ROUTE_NAME = 'routeName'
  82. NETWORK_INTERFACE_NAME = 'networkInterfaceName'
  83. LOCATION_NAME = 'locationName'
  84. class AzureSecurityGroup(BaseSecurityGroup):
  85. def __init__(self, provider, security_group):
  86. super(AzureSecurityGroup, self).__init__(provider, security_group)
  87. self._security_group = security_group
  88. if not self._security_group.tags:
  89. self._security_group.tags = {}
  90. @property
  91. def network_id(self):
  92. return self._security_group.resource_guid
  93. @property
  94. def resource_name(self):
  95. return self._security_group.name
  96. @property
  97. def name(self):
  98. return self._security_group.tags.get('Name', self._security_group.name)
  99. @name.setter
  100. def name(self, value):
  101. self._security_group.tags.update(Name=value)
  102. self._provider.azure_client. \
  103. update_security_group_tags(self.resource_name,
  104. self._security_group.tags)
  105. @property
  106. def description(self):
  107. return self._security_group.tags.get('Description', None)
  108. @description.setter
  109. def description(self, value):
  110. self._security_group.tags.update(Description=value)
  111. self._provider.azure_client.\
  112. update_security_group_tags(self.resource_name,
  113. self._security_group.tags)
  114. @property
  115. def rules(self):
  116. """
  117. The default rules are not returned, only custom rules.
  118. """
  119. security_group_rules = []
  120. for custom_rule in self._security_group.security_rules:
  121. sg_custom_rule = AzureSecurityGroupRule(self._provider,
  122. custom_rule, self)
  123. security_group_rules.append(sg_custom_rule)
  124. return security_group_rules
  125. def delete(self):
  126. try:
  127. self._provider.azure_client.\
  128. delete_security_group(self.resource_name)
  129. return True
  130. except CloudError as cloudError:
  131. log.exception(cloudError.message)
  132. return False
  133. def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
  134. cidr_ip=None, src_group=None):
  135. """
  136. Create a security group rule.
  137. You need to pass in either ``src_group`` OR ``ip_protocol``,
  138. ``from_port``, ``to_port``, and ``cidr_ip``. In other words, either
  139. you are authorizing another group or you are authorizing some
  140. ip-based rule.
  141. :type ip_protocol: str
  142. :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
  143. :type from_port: int
  144. :param from_port: The beginning port number you are enabling
  145. :type to_port: int
  146. :param to_port: The ending port number you are enabling
  147. :type cidr_ip: str or list of strings
  148. :param cidr_ip: The CIDR block you are providing access to.
  149. :type src_group: ``object`` of :class:`.SecurityGroup`
  150. :param src_group: The Security Group you are granting access to.
  151. :rtype: :class:``.SecurityGroupRule``
  152. :return: Rule object if successful or ``None``.
  153. """
  154. # If cidr_ip is None, default values is set as 0.0.0.0/0
  155. if not cidr_ip:
  156. cidr_ip = '0.0.0.0/0'
  157. # If the SG with same parameters exist already,
  158. # then, it is returned instead of creating a new one.
  159. rule = self.get_rule(ip_protocol, from_port,
  160. to_port, cidr_ip, src_group)
  161. if not rule:
  162. # resource_group = self._provider.resource_group
  163. count = len(self.rules) + 1
  164. rule_name = "Rule - " + str(count)
  165. priority = count * 100
  166. destination_port_range = "*"
  167. destination_address_prefix = "*"
  168. access = "Allow"
  169. direction = "Inbound"
  170. parameters = {"protocol": ip_protocol,
  171. "source_port_range":
  172. str(from_port) + "-" + str(to_port),
  173. "destination_port_range": destination_port_range,
  174. "priority": priority,
  175. "source_address_prefix": cidr_ip,
  176. "destination_address_prefix":
  177. destination_address_prefix,
  178. "access": access, "direction": direction}
  179. result = self._provider.azure_client. \
  180. create_security_group_rule(self.resource_name,
  181. rule_name, parameters)
  182. self._security_group.security_rules.append(result)
  183. return AzureSecurityGroupRule(self._provider, result, self)
  184. return rule
  185. def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
  186. cidr_ip=None, src_group=None):
  187. for rule in self.rules:
  188. if (rule.ip_protocol == ip_protocol and
  189. rule.from_port == str(from_port) and
  190. rule.to_port == str(to_port) and
  191. rule.cidr_ip == cidr_ip):
  192. return rule
  193. return None
  194. def to_json(self):
  195. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  196. js = {k: v for (k, v) in attr if not k.startswith('_')}
  197. json_rules = [r.to_json() for r in self.rules]
  198. js['rules'] = [json.loads(r) for r in json_rules]
  199. if js.get('network_id'):
  200. js.pop('network_id') # Omit for consistency across cloud providers
  201. return json.dumps(js, sort_keys=True)
  202. class AzureSecurityGroupRule(BaseSecurityGroupRule):
  203. def __init__(self, provider, rule, parent):
  204. super(AzureSecurityGroupRule, self).__init__(provider, rule, parent)
  205. @property
  206. def name(self):
  207. return self._rule.name
  208. @property
  209. def id(self):
  210. return self._rule.id
  211. @property
  212. def ip_protocol(self):
  213. return self._rule.protocol
  214. @property
  215. def from_port(self):
  216. if self._rule.source_port_range == '*':
  217. return self._rule.source_port_range
  218. source_port_range = self._rule.source_port_range
  219. port_range_split = source_port_range.split('-', 1)
  220. return port_range_split[0]
  221. @property
  222. def to_port(self):
  223. if self._rule.source_port_range == '*':
  224. return self._rule.source_port_range
  225. source_port_range = self._rule.source_port_range
  226. port_range_split = source_port_range.split('-', 1)
  227. return port_range_split[1]
  228. @property
  229. def cidr_ip(self):
  230. return self._rule.source_address_prefix
  231. @property
  232. def group(self):
  233. return self.parent
  234. def to_json(self):
  235. attr = inspect.getmembers(self, lambda a: not (inspect.isroutine(a)))
  236. js = {k: v for (k, v) in attr if not k.startswith('_')}
  237. js['group'] = self.group.id if self.group else ''
  238. js['parent'] = self.parent.id if self.parent else ''
  239. return json.dumps(js, sort_keys=True)
  240. def delete(self):
  241. security_group = self.parent.name
  242. self._provider.azure_client. \
  243. delete_security_group_rule(self.name, security_group)
  244. for i, o in enumerate(self.parent._security_group.security_rules):
  245. if o.name == self.name:
  246. del self.parent._security_group.security_rules[i]
  247. break
  248. class AzureBucketObject(BaseBucketObject):
  249. def __init__(self, provider, container, key):
  250. super(AzureBucketObject, self).__init__(provider)
  251. self._container = container
  252. self._key = key
  253. @property
  254. def id(self):
  255. return self._key.name
  256. @property
  257. def name(self):
  258. """
  259. Get this object's name.
  260. """
  261. return self._key.name
  262. @property
  263. def size(self):
  264. """
  265. Get this object's size.
  266. """
  267. return self._key.properties.content_length
  268. @property
  269. def last_modified(self):
  270. """
  271. Get the date and time this object was last modified.
  272. """
  273. return self._key.properties.last_modified. \
  274. strftime("%Y-%m-%dT%H:%M:%S.%f")
  275. def iter_content(self):
  276. """
  277. Returns this object's content as an
  278. iterable.
  279. """
  280. content_stream = self._provider.azure_client. \
  281. get_blob_content(self._container.name, self._key.name)
  282. if content_stream:
  283. content_stream.seek(0)
  284. return content_stream
  285. def upload(self, data):
  286. """
  287. Set the contents of this object to the data read from the source
  288. string.
  289. """
  290. try:
  291. self._provider.azure_client.create_blob_from_text(
  292. self._container.name, self.name, data)
  293. return True
  294. except AzureException as azureEx:
  295. log.exception(azureEx)
  296. return False
  297. def upload_from_file(self, path):
  298. """
  299. Store the contents of the file pointed by the "path" variable.
  300. """
  301. try:
  302. self._provider.azure_client.create_blob_from_file(
  303. self._container.name, self.name, path)
  304. return True
  305. except AzureException as azureEx:
  306. log.exception(azureEx)
  307. return False
  308. def delete(self):
  309. """
  310. Delete this object.
  311. :rtype: bool
  312. :return: True if successful
  313. """
  314. try:
  315. self._provider.azure_client.delete_blob(
  316. self._container.name, self.name)
  317. return True
  318. except AzureException as azureEx:
  319. log.exception(azureEx)
  320. return False
  321. def generate_url(self, expires_in=0):
  322. """
  323. Generate a URL to this object.
  324. """
  325. return self._provider.azure_client.get_blob_url(
  326. self._container.name, self.name)
  327. class AzureBucket(BaseBucket):
  328. def __init__(self, provider, bucket):
  329. super(AzureBucket, self).__init__(provider)
  330. self._bucket = bucket
  331. @property
  332. def id(self):
  333. return self._bucket.name
  334. @property
  335. def name(self):
  336. """
  337. Get this bucket's name.
  338. """
  339. return self._bucket.name
  340. def get(self, key):
  341. """
  342. Retrieve a given object from this bucket.
  343. """
  344. try:
  345. obj = self._provider.azure_client.get_blob(self.name, key)
  346. return AzureBucketObject(self._provider, self, obj)
  347. except AzureException as azureEx:
  348. log.exception(azureEx)
  349. return None
  350. def list(self, limit=None, marker=None, prefix=None):
  351. """
  352. List all objects within this bucket.
  353. :rtype: BucketObject
  354. :return: List of all available BucketObjects within this bucket.
  355. """
  356. objects = [AzureBucketObject(self._provider, self, obj)
  357. for obj in
  358. self._provider.azure_client.list_blobs(
  359. self.name, prefix=prefix)]
  360. return ClientPagedResultList(self._provider, objects,
  361. limit=limit, marker=marker)
  362. def delete(self, delete_contents=True):
  363. """
  364. Delete this bucket.
  365. """
  366. try:
  367. self._provider.azure_client.delete_container(self.name)
  368. return True
  369. except AzureException as azureEx:
  370. log.exception(azureEx)
  371. return False
  372. def create_object(self, name):
  373. self._provider.azure_client.create_blob_from_text(
  374. self.name, name, '')
  375. return self.get(name)
  376. def exists(self, name):
  377. """
  378. Determine if an object with given name exists in this bucket.
  379. """
  380. return True if self.get(name) else False
  381. class AzureVolume(BaseVolume):
  382. VOLUME_STATE_MAP = {
  383. 'InProgress': VolumeState.CREATING,
  384. 'Creating': VolumeState.CREATING,
  385. 'Unattached': VolumeState.AVAILABLE,
  386. 'Attached': VolumeState.IN_USE,
  387. 'Deleting': VolumeState.CONFIGURING,
  388. 'Updating': VolumeState.CONFIGURING,
  389. 'Deleted': VolumeState.DELETED,
  390. 'Failed': VolumeState.ERROR,
  391. 'Canceled': VolumeState.ERROR
  392. }
  393. def __init__(self, provider, volume):
  394. super(AzureVolume, self).__init__(provider)
  395. self._volume = volume
  396. self._url_params = azure_helpers. \
  397. parse_url(VOLUME_RESOURCE_ID, volume.id)
  398. self._description = None
  399. self._status = 'unknown'
  400. self.update_status()
  401. if not self._volume.tags:
  402. self._volume.tags = {}
  403. def update_status(self):
  404. if not self._volume.provisioning_state == 'Succeeded':
  405. self._status = self._volume.provisioning_state
  406. elif self._volume.owner_id:
  407. self._status = 'Attached'
  408. else:
  409. self._status = 'Unattached'
  410. @property
  411. def id(self):
  412. return self._volume.id
  413. @property
  414. def resource_name(self):
  415. return self._volume.name
  416. @property
  417. def tags(self):
  418. return self._volume.tags
  419. @property
  420. def name(self):
  421. """
  422. Get the volume name.
  423. .. note:: an instance must have a (case sensitive) tag ``Name``
  424. """
  425. return self._volume.tags.get('Name', self._volume.name)
  426. @name.setter
  427. # pylint:disable=arguments-differ
  428. def name(self, value):
  429. """
  430. Set the volume name.
  431. """
  432. # self._volume.name = value
  433. self._volume.tags.update(Name=value)
  434. self._provider.azure_client. \
  435. update_disk_tags(self.resource_name,
  436. self._volume.tags)
  437. @property
  438. def description(self):
  439. return self._volume.tags.get('Description', None)
  440. @description.setter
  441. def description(self, value):
  442. self._volume.tags.update(Description=value)
  443. self._provider.azure_client. \
  444. update_disk_tags(self.resource_name,
  445. self._volume.tags)
  446. @property
  447. def size(self):
  448. return self._volume.disk_size_gb
  449. @property
  450. def create_time(self):
  451. return self._volume.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  452. @property
  453. def zone_id(self):
  454. return self._volume.location
  455. @property
  456. def source(self):
  457. if self._volume.creation_data.source_uri:
  458. return self._provider.block_store.snapshots. \
  459. get(self._volume.creation_data.source_uri)
  460. return None
  461. @property
  462. def attachments(self):
  463. if self._volume.owner_id:
  464. return BaseAttachmentInfo(self,
  465. self._volume.owner_id,
  466. None)
  467. else:
  468. return None
  469. def attach(self, instance, device=None):
  470. """
  471. Attach this volume to an instance.
  472. """
  473. try:
  474. instance_id = instance.id if isinstance(
  475. instance,
  476. Instance) else instance
  477. params = azure_helpers.parse_url(INSTANCE_RESOURCE_ID,
  478. instance_id)
  479. vm = self._provider.azure_client.get_vm(params.get(VM_NAME))
  480. vm.storage_profile.data_disks.append({
  481. 'lun': len(vm.storage_profile.data_disks),
  482. 'name': self.resource_name,
  483. 'create_option': 'attach',
  484. 'managed_disk': {
  485. 'id': self.id
  486. }
  487. })
  488. self._provider.azure_client \
  489. .create_vm(params.get(VM_NAME), vm)
  490. return True
  491. except CloudError as cloudError:
  492. log.exception(cloudError.message)
  493. return False
  494. def detach(self, force=False):
  495. """
  496. Detach this volume from an instance.
  497. """
  498. virtual_machine = None
  499. for index, vm in enumerate(
  500. self._provider.azure_client.list_vm()):
  501. for item in vm.storage_profile.data_disks:
  502. if item.managed_disk and item.managed_disk.id == self.id:
  503. vm.storage_profile.data_disks.remove(item)
  504. virtual_machine = vm
  505. break
  506. if virtual_machine:
  507. break
  508. if virtual_machine:
  509. self._provider.azure_client.create_vm(
  510. virtual_machine.name,
  511. virtual_machine
  512. )
  513. return True
  514. return False
  515. def create_snapshot(self, name, description=None):
  516. """
  517. Create a snapshot of this Volume.
  518. """
  519. return self._provider.block_store.snapshots.create(name, self.id)
  520. def delete(self):
  521. """
  522. Delete this volume.
  523. """
  524. try:
  525. self._provider.azure_client. \
  526. delete_disk(self.resource_name)
  527. return True
  528. except CloudError as cloudError:
  529. log.exception(cloudError.message)
  530. return False
  531. @property
  532. def state(self):
  533. return AzureVolume.VOLUME_STATE_MAP.get(
  534. self._status, VolumeState.UNKNOWN)
  535. def refresh(self):
  536. """
  537. Refreshes the state of this volume by re-querying the cloud provider
  538. for its latest state.
  539. """
  540. try:
  541. self._volume = self._provider.azure_client. \
  542. get_disk(self.resource_name)
  543. self.update_status()
  544. except (CloudError, ValueError) as cloudError:
  545. log.exception(cloudError.message)
  546. # The volume no longer exists and cannot be refreshed.
  547. # set the status to unknown
  548. self._status = 'unknown'
  549. class AzureSnapshot(BaseSnapshot):
  550. SNAPSHOT_STATE_MAP = {
  551. 'InProgress': SnapshotState.PENDING,
  552. 'Succeeded': SnapshotState.AVAILABLE,
  553. 'Failed': SnapshotState.ERROR,
  554. 'Canceled': SnapshotState.ERROR,
  555. 'Updating': SnapshotState.CONFIGURING,
  556. 'Deleting': SnapshotState.CONFIGURING,
  557. 'Deleted': SnapshotState.UNKNOWN
  558. }
  559. def __init__(self, provider, snapshot):
  560. super(AzureSnapshot, self).__init__(provider)
  561. self._snapshot = snapshot
  562. self._description = None
  563. self._url_params = azure_helpers. \
  564. parse_url(SNAPSHOT_RESOURCE_ID, snapshot.id)
  565. self._status = self._snapshot.provisioning_state
  566. if not self._snapshot.tags:
  567. self._snapshot.tags = {}
  568. @property
  569. def id(self):
  570. return self._snapshot.id
  571. @property
  572. def resource_name(self):
  573. return self._snapshot.name
  574. @property
  575. def name(self):
  576. """
  577. Get the snapshot name.
  578. .. note:: an instance must have a (case sensitive) tag ``Name``
  579. """
  580. return self._snapshot.tags.get('Name', self._snapshot.name)
  581. @name.setter
  582. # pylint:disable=arguments-differ
  583. def name(self, value):
  584. """
  585. Set the snapshot name.
  586. """
  587. # self._snapshot.name = value
  588. self._snapshot.tags.update(Name=value)
  589. self._provider.azure_client. \
  590. update_snapshot_tags(self.resource_name,
  591. self._snapshot.tags)
  592. @property
  593. def description(self):
  594. return self._snapshot.tags.get('Description', None)
  595. @description.setter
  596. def description(self, value):
  597. self._snapshot.tags.update(Description=value)
  598. self._provider.azure_client. \
  599. update_snapshot_tags(self.resource_name,
  600. self._snapshot.tags)
  601. @property
  602. def size(self):
  603. return self._snapshot.disk_size_gb
  604. @property
  605. def volume_id(self):
  606. return self._snapshot.creation_data.source_uri
  607. @property
  608. def create_time(self):
  609. return self._snapshot.time_created.strftime("%Y-%m-%dT%H:%M:%S.%f")
  610. @property
  611. def state(self):
  612. return AzureSnapshot.SNAPSHOT_STATE_MAP.get(
  613. self._status, SnapshotState.UNKNOWN)
  614. def refresh(self):
  615. """
  616. Refreshes the state of this snapshot by re-querying the cloud provider
  617. for its latest state.
  618. """
  619. try:
  620. self._snapshot = self._provider.azure_client. \
  621. get_snapshot(self.resource_name)
  622. self._status = self._snapshot.provisioning_state
  623. except (CloudError, ValueError) as cloudError:
  624. log.exception(cloudError.message)
  625. # The snapshot no longer exists and cannot be refreshed.
  626. # set the status to unknown
  627. self._status = 'unknown'
  628. def delete(self):
  629. """
  630. Delete this snapshot.
  631. """
  632. try:
  633. self._provider.azure_client.delete_snapshot(self.resource_name)
  634. return True
  635. except CloudError as cloudError:
  636. log.exception(cloudError.message)
  637. return False
  638. def create_volume(self, placement=None,
  639. size=None, volume_type=None, iops=None):
  640. """
  641. Create a new Volume from this Snapshot.
  642. """
  643. return self._provider.block_store.volumes. \
  644. create(self.resource_name, self.size,
  645. zone=placement, snapshot=self)
  646. class AzureMachineImage(BaseMachineImage):
  647. IMAGE_STATE_MAP = {
  648. 'InProgress': MachineImageState.PENDING,
  649. 'Succeeded': MachineImageState.AVAILABLE,
  650. 'Failed': MachineImageState.ERROR
  651. }
  652. def __init__(self, provider, image):
  653. super(AzureMachineImage, self).__init__(provider)
  654. self._image = image
  655. self._url_params = azure_helpers. \
  656. parse_url(IMAGE_RESOURCE_ID, image.id)
  657. self._state = self._image.provisioning_state
  658. if not self._image.tags:
  659. self._image.tags = {}
  660. @property
  661. def id(self):
  662. """
  663. Get the image identifier.
  664. :rtype: ``str``
  665. :return: ID for this instance as returned by the cloud middleware.
  666. """
  667. return self._image.id
  668. @property
  669. def name(self):
  670. """
  671. Get the image name.
  672. :rtype: ``str``
  673. :return: Name for this image as returned by the cloud middleware.
  674. """
  675. return self._image.tags.get('Name', self._image.name)
  676. @name.setter
  677. def name(self, value):
  678. """
  679. Set the image name.
  680. """
  681. self._image.tags.update(Name=value)
  682. self._provider.azure_client. \
  683. update_image_tags(self.resource_name, self._image.tags)
  684. @property
  685. def description(self):
  686. """
  687. Get the image description.
  688. :rtype: ``str``
  689. :return: Description for this image as returned by the cloud middleware
  690. """
  691. return self._image.tags.get('Description', None)
  692. @description.setter
  693. def description(self, value):
  694. """
  695. Set the image name.
  696. """
  697. self._image.tags.update(Description=value)
  698. self._provider.azure_client. \
  699. update_image_tags(self.resource_name, self._image.tags)
  700. @property
  701. def resource_name(self):
  702. return self._image.name
  703. @property
  704. def min_disk(self):
  705. """
  706. Returns the minimum size of the disk that's required to
  707. boot this image (in GB)
  708. :rtype: ``int``
  709. :return: The minimum disk size needed by this image
  710. """
  711. return self._image.storage_profile.os_disk.disk_size_gb
  712. @property
  713. def os_type(self):
  714. return self._image.storage_profile.os_disk.os_type.value
  715. def delete(self):
  716. """
  717. Delete this image
  718. """
  719. self._provider.azure_client.delete_image(self.resource_name)
  720. @property
  721. def state(self):
  722. return AzureMachineImage.IMAGE_STATE_MAP.get(
  723. self._state, MachineImageState.UNKNOWN)
  724. def refresh(self):
  725. """
  726. Refreshes the state of this instance by re-querying the cloud provider
  727. for its latest state.
  728. """
  729. try:
  730. self._image = self._provider.azure_client\
  731. .get_image(self.resource_name)
  732. self._state = self._image.provisioning_state
  733. except CloudError as cloudError:
  734. log.exception(cloudError.message)
  735. # image no longer exists
  736. self._state = "unknown"
  737. class AzureNetwork(BaseNetwork):
  738. NETWORK_STATE_MAP = {
  739. 'InProgress': NetworkState.PENDING,
  740. 'Succeeded': NetworkState.AVAILABLE,
  741. }
  742. def __init__(self, provider, network):
  743. super(AzureNetwork, self).__init__(provider)
  744. self._network = network
  745. self._state = self._network.provisioning_state
  746. if not self._network.tags:
  747. self._network.tags = {}
  748. @property
  749. def id(self):
  750. return self._network.id
  751. @property
  752. def name(self):
  753. """
  754. Get the network name.
  755. .. note:: the network must have a (case sensitive) tag ``Name``
  756. """
  757. return self._network.tags.get('Name', self._network.name)
  758. @property
  759. def resource_name(self):
  760. return self._network.name
  761. @name.setter
  762. # pylint:disable=arguments-differ
  763. def name(self, value):
  764. """
  765. Set the network name.
  766. """
  767. self._network.tags.update(Name=value)
  768. self._provider.azure_client.\
  769. update_network_tags(self.resource_name, self._network.tags)
  770. @property
  771. def external(self):
  772. """
  773. For Azure, all VPC networks can be connected to the Internet so always
  774. return ``True``.
  775. """
  776. return True
  777. @property
  778. def state(self):
  779. return AzureNetwork.NETWORK_STATE_MAP.get(
  780. self._state, NetworkState.UNKNOWN)
  781. def refresh(self):
  782. """
  783. Refreshes the state of this network by re-querying the cloud provider
  784. for its latest state.
  785. """
  786. try:
  787. self._network = self._provider.azure_client.\
  788. get_network(self.resource_name)
  789. self._state = self._network.provisioning_state
  790. except (CloudError, ValueError) as cloudError:
  791. log.exception(cloudError.message)
  792. # The network no longer exists and cannot be refreshed.
  793. # set the status to unknown
  794. self._state = 'unknown'
  795. @property
  796. def cidr_block(self):
  797. return self._network.address_space.address_prefixes[0]
  798. def delete(self):
  799. """
  800. Delete an existing network.
  801. """
  802. try:
  803. self._provider.azure_client.\
  804. delete_network(self.resource_name)
  805. return True
  806. except CloudError as cloudError:
  807. log.exception(cloudError.message)
  808. return False
  809. def subnets(self):
  810. return self._provider.network.subnets.list(network=self.id)
  811. def create_subnet(self, cidr_block, name=None, zone=None):
  812. return self._provider.network.subnets.\
  813. create(network=self.id, cidr_block=cidr_block, name=name)
  814. class AzureFloatingIP(BaseFloatingIP):
  815. def __init__(self, provider, floating_ip):
  816. super(AzureFloatingIP, self).__init__(provider)
  817. self._ip = floating_ip
  818. @property
  819. def id(self):
  820. return self._ip.id
  821. @property
  822. def public_ip(self):
  823. return self._ip.ip_address
  824. @property
  825. def private_ip(self):
  826. return self._ip.ip_configuration.private_ip_address \
  827. if self._ip.ip_configuration else None
  828. def in_use(self):
  829. return True if self._ip.ip_configuration else False
  830. def delete(self):
  831. """
  832. Delete an existing floating ip.
  833. """
  834. try:
  835. self._provider.azure_client.delete_floating_ip(self._ip.name)
  836. return True
  837. except CloudError as cloudError:
  838. log.exception(cloudError.message)
  839. return False
  840. class AzureRegion(BaseRegion):
  841. def __init__(self, provider, azure_region):
  842. super(AzureRegion, self).__init__(provider)
  843. self._azure_region = azure_region
  844. @property
  845. def id(self):
  846. return self._azure_region.id
  847. @property
  848. def name(self):
  849. return self._azure_region.name
  850. @property
  851. def zones(self):
  852. """
  853. Access information about placement zones within this region.
  854. """
  855. # As Azure does not have zones feature, mapping the region
  856. # information in the zones.
  857. return [AzurePlacementZone(self._provider,
  858. self._azure_region.id,
  859. self._azure_region.name)]
  860. class AzurePlacementZone(BasePlacementZone):
  861. """
  862. As Azure does not provide zones (limited support), we are mapping the
  863. region information in the zones.
  864. """
  865. def __init__(self, provider, zone, region):
  866. super(AzurePlacementZone, self).__init__(provider)
  867. self._azure_zone = zone
  868. self._azure_region = region
  869. @property
  870. def id(self):
  871. """
  872. Get the zone id
  873. :rtype: ``str``
  874. :return: ID for this zone as returned by the cloud middleware.
  875. """
  876. return self._azure_zone
  877. @property
  878. def name(self):
  879. """
  880. Get the zone name.
  881. :rtype: ``str``
  882. :return: Name for this zone as returned by the cloud middleware.
  883. """
  884. return self._azure_region
  885. @property
  886. def region_name(self):
  887. """
  888. Get the region that this zone belongs to.
  889. :rtype: ``str``
  890. :return: Name of this zone's region as returned by the
  891. cloud middleware
  892. """
  893. return self._azure_region
  894. class AzureSubnet(BaseSubnet):
  895. def __init__(self, provider, subnet):
  896. super(AzureSubnet, self).__init__(provider)
  897. self._subnet = subnet
  898. self._url_params = azure_helpers\
  899. .parse_url(SUBNET_RESOURCE_ID, subnet.id)
  900. self._network = self._provider.azure_client.\
  901. get_network(self._url_params.get(NETWORK_NAME))
  902. @property
  903. def id(self):
  904. return self._subnet.id
  905. @property
  906. def name(self):
  907. """
  908. Get the subnet name.
  909. .. note:: the subnet must have a (case sensitive) tag ``Name``
  910. """
  911. return self._subnet.name
  912. @property
  913. def zone(self):
  914. regions = [region
  915. for region in self._provider.
  916. compute.regions.list()
  917. if region.name == self._network.location]
  918. return regions[0].zones[0]
  919. @property
  920. def cidr_block(self):
  921. return self._subnet.address_prefix
  922. @property
  923. def network_id(self):
  924. return self._network.id
  925. def delete(self):
  926. try:
  927. self._provider.azure_client.\
  928. delete_subnet(self._url_params.get(NETWORK_NAME),
  929. self._url_params.get(SUBNET_NAME))
  930. return True
  931. except CloudError as cloudError:
  932. log.exception(cloudError.message)
  933. return False
  934. class AzureInstance(BaseInstance):
  935. INSTANCE_STATE_MAP = {
  936. 'InProgress': InstanceState.PENDING,
  937. 'Creating': InstanceState.PENDING,
  938. 'VM running': InstanceState.RUNNING,
  939. 'Updating': InstanceState.CONFIGURING,
  940. 'Deleted': InstanceState.TERMINATED,
  941. 'Stopping': InstanceState.CONFIGURING,
  942. 'Deleting': InstanceState.CONFIGURING,
  943. 'Stopped': InstanceState.STOPPED,
  944. 'Canceled': InstanceState.ERROR,
  945. 'Failed': InstanceState.ERROR,
  946. 'VM stopped': InstanceState.STOPPED,
  947. 'VM deallocated': InstanceState.STOPPED,
  948. 'VM deallocating': InstanceState.CONFIGURING,
  949. 'VM stopping': InstanceState.CONFIGURING,
  950. 'VM starting': InstanceState.CONFIGURING
  951. }
  952. def __init__(self, provider, vm_instance):
  953. super(AzureInstance, self).__init__(provider)
  954. self._vm = vm_instance
  955. self._url_params = azure_helpers.\
  956. parse_url(INSTANCE_RESOURCE_ID, vm_instance.id)
  957. self.update_state()
  958. self._get_network_attributes()
  959. if not self._vm.tags:
  960. self._vm.tags = {}
  961. def _get_network_attributes(self):
  962. self._private_ips = []
  963. self._public_ips = []
  964. self._security_group_ids = []
  965. self._public_ip_ids = []
  966. self._nic_ids = []
  967. for nic in self._vm.network_profile.network_interfaces:
  968. self._nic_ids.append(nic.id)
  969. nic_params = azure_helpers.\
  970. parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic.id)
  971. nic = self._provider.azure_client.\
  972. get_nic(nic_params.get(NETWORK_INTERFACE_NAME))
  973. if nic.network_security_group:
  974. self._security_group_ids.append(nic.network_security_group.id)
  975. if nic.ip_configurations:
  976. for ip_config in nic.ip_configurations:
  977. self._private_ips.append(ip_config.private_ip_address)
  978. if ip_config.public_ip_address:
  979. url_params = azure_helpers.\
  980. parse_url(PUBLIC_IP_RESOURCE_ID,
  981. ip_config.public_ip_address.id)
  982. public_ip = self._provider.\
  983. azure_client.\
  984. get_public_ip(url_params.get(PUBLIC_IP_NAME))
  985. self._public_ip_ids.append(public_ip.id)
  986. self._public_ips.append(public_ip.ip_address)
  987. @property
  988. def id(self):
  989. """
  990. Get the instance identifier.
  991. """
  992. return self._vm.id
  993. @property
  994. def resource_name(self):
  995. return self._vm.name
  996. @property
  997. def name(self):
  998. """
  999. Get the instance name.
  1000. .. note:: an instance must have a (case sensitive) tag ``Name``
  1001. """
  1002. return self._vm.tags.get('Name', self._vm.name)
  1003. @name.setter
  1004. # pylint:disable=arguments-differ
  1005. def name(self, value):
  1006. """
  1007. Set the instance name.
  1008. """
  1009. self._vm.tags.update(Name=value)
  1010. self._provider.azure_client.\
  1011. update_vm_tags(self.resource_name, self._vm.tags)
  1012. @property
  1013. def public_ips(self):
  1014. """
  1015. Get all the public IP addresses for this instance.
  1016. """
  1017. return self._public_ips
  1018. @property
  1019. def private_ips(self):
  1020. """
  1021. Get all the private IP addresses for this instance.
  1022. """
  1023. return self._private_ips
  1024. @property
  1025. def instance_type_id(self):
  1026. """
  1027. Get the instance type name.
  1028. """
  1029. return self._vm.hardware_profile.vm_size
  1030. @property
  1031. def instance_type(self):
  1032. """
  1033. Get the instance type.
  1034. """
  1035. return self._provider.compute.instance_types.find(
  1036. name=self.instance_type_id)[0]
  1037. def reboot(self):
  1038. """
  1039. Reboot this instance (using the cloud middleware API).
  1040. """
  1041. self._provider.azure_client.restart_vm(self.resource_name)
  1042. def terminate(self):
  1043. """
  1044. Permanently terminate this instance.
  1045. """
  1046. self._provider.azure_client.deallocate_vm(self.resource_name)
  1047. self._provider.azure_client.delete_vm(self.resource_name)
  1048. for nic_id in self._nic_ids:
  1049. nic_id_params = azure_helpers.\
  1050. parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
  1051. self._provider.azure_client.\
  1052. delete_nic(nic_id_params.get(NETWORK_INTERFACE_NAME))
  1053. for public_ip_id in self._public_ip_ids:
  1054. public_ip_id_params = azure_helpers.\
  1055. parse_url(PUBLIC_IP_RESOURCE_ID, public_ip_id)
  1056. self._provider.azure_client.\
  1057. delete_public_ip(public_ip_id_params.get(PUBLIC_IP_NAME))
  1058. for data_disk in self._vm.storage_profile.data_disks:
  1059. if data_disk.managed_disk:
  1060. disk_params = azure_helpers.\
  1061. parse_url(VOLUME_RESOURCE_ID,
  1062. data_disk.managed_disk.id)
  1063. disk = self._provider.azure_client.\
  1064. get_disk(disk_params.get(VOLUME_NAME))
  1065. if disk and disk.tags \
  1066. and disk.tags.get('delete_on_terminate',
  1067. 'False') == 'True':
  1068. self._provider.azure_client.\
  1069. delete_disk(disk_params.get(VOLUME_NAME))
  1070. if self._vm.storage_profile.os_disk.managed_disk:
  1071. disk_params = azure_helpers. \
  1072. parse_url(VOLUME_RESOURCE_ID,
  1073. self._vm.storage_profile.os_disk.managed_disk.id)
  1074. self._provider.azure_client. \
  1075. delete_disk(disk_params.get(VOLUME_NAME))
  1076. @property
  1077. def image_id(self):
  1078. """
  1079. Get the image ID for this instance.
  1080. """
  1081. return self._vm.storage_profile.image_reference.id
  1082. @property
  1083. def zone_id(self):
  1084. """
  1085. Get the placement zone id where this instance is running.
  1086. """
  1087. return self._vm.location
  1088. @property
  1089. def security_groups(self):
  1090. """
  1091. Get the security groups associated with this instance.
  1092. """
  1093. return [self._provider.security.security_groups.get(group_id)
  1094. for group_id in self._security_group_ids]
  1095. @property
  1096. def security_group_ids(self):
  1097. """
  1098. Get the security groups IDs associated with this instance.
  1099. """
  1100. return self._security_group_ids
  1101. @property
  1102. def key_pair_name(self):
  1103. """
  1104. Get the name of the key pair associated with this instance.
  1105. """
  1106. return self._vm.tags.get('Key_Pair')
  1107. def create_image(self, name, private_key_path=None):
  1108. """
  1109. Create a new image based on this instance.
  1110. """
  1111. if not self._state == 'VM generalized':
  1112. if not self._state == 'VM running':
  1113. self._provider.azure_client.start_vm(self.resource_name)
  1114. time.sleep(60)
  1115. self._get_network_attributes()
  1116. if private_key_path:
  1117. self._deprovision(private_key_path)
  1118. self._provider.azure_client.deallocate_vm(self.resource_name)
  1119. self._provider.azure_client.generalize_vm(self.resource_name)
  1120. create_params = {
  1121. 'location': self._provider.region_name,
  1122. 'source_virtual_machine': {
  1123. 'id': self.id
  1124. },
  1125. 'tags': {'Name': name}
  1126. }
  1127. self._provider.azure_client.\
  1128. create_image(name, create_params)
  1129. image = self._provider.azure_client.\
  1130. get_image(name)
  1131. return AzureMachineImage(self._provider, image)
  1132. def _deprovision(self, private_key_path):
  1133. cnopts = pysftp.CnOpts()
  1134. cnopts.hostkeys = None
  1135. with pysftp.Connection(self.public_ips[0],
  1136. username=self._provider.default_user_name,
  1137. cnopts=cnopts,
  1138. private_key=private_key_path) as sftp:
  1139. sftp.execute('sudo waagent -deprovision -force')
  1140. sftp.close()
  1141. def add_floating_ip(self, ip_address):
  1142. try:
  1143. ip_addresses = [ip for ip in self._provider.
  1144. azure_client.list_floating_ips()
  1145. if ip.ip_address and ip.ip_address == ip_address]
  1146. if len(ip_addresses) > 0:
  1147. """
  1148. Add an elastic IP address to this instance.
  1149. """
  1150. nic_params = azure_helpers. \
  1151. parse_url(NETWORK_INTERFACE_RESOURCE_ID,
  1152. self._vm.network_profile.
  1153. network_interfaces[0].id)
  1154. nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
  1155. nic = self._provider.azure_client.get_nic(nic_name)
  1156. nic.ip_configurations[0].public_ip_address = {
  1157. 'id': ip_addresses[0].id
  1158. }
  1159. self._provider.azure_client.\
  1160. create_nic(nic_name, nic)
  1161. return True
  1162. return False
  1163. except CloudError as cloudError:
  1164. log.exception(cloudError.message)
  1165. return False
  1166. def remove_floating_ip(self, ip_address=None):
  1167. """
  1168. Remove a elastic IP address from this instance.
  1169. """
  1170. try:
  1171. nic_params = azure_helpers. \
  1172. parse_url(NETWORK_INTERFACE_RESOURCE_ID,
  1173. self._vm.network_profile.network_interfaces[0].id)
  1174. nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
  1175. nic = self._provider.azure_client.get_nic(nic_name)
  1176. nic.ip_configurations[0].public_ip_address = None
  1177. self._provider.azure_client. \
  1178. create_nic(nic_name, nic)
  1179. return True
  1180. except CloudError as cloudError:
  1181. log.exception(cloudError.message)
  1182. return False
  1183. def add_security_group(self, sg):
  1184. '''
  1185. :param sg:
  1186. :return: None
  1187. This method adds the security group to VM instance.
  1188. In Azure, security group added to Network interface.
  1189. Azure supports to add only one security group to
  1190. network interface, we are adding the provided security group 4
  1191. if not associated any security group to NIC
  1192. else replacing the existing security group.
  1193. '''
  1194. nic_id = \
  1195. self._vm.network_profile.network_interfaces[0].id
  1196. nic_params = azure_helpers.\
  1197. parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
  1198. nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
  1199. nic = self._provider.azure_client.get_nic(nic_name)
  1200. if not nic.network_security_group:
  1201. nic.network_security_group = NetworkSecurityGroup()
  1202. sg_id = sg.id if isinstance(sg, AzureSecurityGroup) else sg
  1203. nic.network_security_group.id = sg_id
  1204. self._provider.azure_client.\
  1205. create_nic(nic_name, nic)
  1206. def remove_security_group(self, sg):
  1207. '''
  1208. :param sg:
  1209. :return: None
  1210. This method removes the security group from VM.
  1211. In Azure, security group added to Network interface.
  1212. Azure supports to add only one security group to
  1213. network interface, we are removing the provided security group
  1214. if it associated with NIC else ignoring.
  1215. '''
  1216. nic_id = \
  1217. self._vm.network_profile.network_interfaces[0].id
  1218. nic_params = azure_helpers. \
  1219. parse_url(NETWORK_INTERFACE_RESOURCE_ID, nic_id)
  1220. nic_name = nic_params.get(NETWORK_INTERFACE_NAME)
  1221. nic = self._provider.azure_client.get_nic(nic_name)
  1222. sg_id = sg.id if isinstance(sg, AzureSecurityGroup) else sg
  1223. if nic.network_security_group and \
  1224. nic.network_security_group.id == sg_id:
  1225. nic.network_security_group = None
  1226. self._provider.azure_client. \
  1227. create_nic(nic_name, nic)
  1228. def update_state(self):
  1229. if self._vm.instance_view and\
  1230. len(self._vm.instance_view.statuses) > 1:
  1231. self._state = \
  1232. self._vm.instance_view.statuses[1].display_status
  1233. else:
  1234. self._state = \
  1235. self._vm.provisioning_state
  1236. @property
  1237. def state(self):
  1238. return AzureInstance.INSTANCE_STATE_MAP.get(
  1239. self._state, InstanceState.UNKNOWN)
  1240. def refresh(self):
  1241. """
  1242. Refreshes the state of this instance by re-querying the cloud provider
  1243. for its latest state.
  1244. """
  1245. try:
  1246. self._vm = self._provider.\
  1247. azure_client.get_vm(self.resource_name)
  1248. if not self._vm.tags:
  1249. self._vm.tags = {}
  1250. self.update_state()
  1251. self._get_network_attributes()
  1252. print(self._state + ' : Vm state')
  1253. except (CloudError, ValueError) as cloudError:
  1254. log.exception(cloudError.message)
  1255. # The volume no longer exists and cannot be refreshed.
  1256. # set the status to unknown
  1257. self._state = 'unknown'
  1258. class AzureLaunchConfig(BaseLaunchConfig):
  1259. def __init__(self, provider):
  1260. super(AzureLaunchConfig, self).__init__(provider)
  1261. class AzureInstanceType(BaseInstanceType):
  1262. def __init__(self, provider, instance_type):
  1263. super(AzureInstanceType, self).__init__(provider)
  1264. self._inst_type = instance_type
  1265. @property
  1266. def id(self):
  1267. return self._inst_type.name
  1268. @property
  1269. def name(self):
  1270. return self._inst_type.name
  1271. @property
  1272. def family(self):
  1273. """
  1274. Python sdk does not return family details.
  1275. So, we are populating it as Unknown
  1276. """
  1277. return "Unknown"
  1278. @property
  1279. def vcpus(self):
  1280. return self._inst_type.number_of_cores
  1281. @property
  1282. def ram(self):
  1283. return self._inst_type.memory_in_mb
  1284. @property
  1285. def size_root_disk(self):
  1286. return self._inst_type.os_disk_size_in_mb / 1024
  1287. @property
  1288. def size_ephemeral_disks(self):
  1289. return self._inst_type.resource_disk_size_in_mb / 1024
  1290. @property
  1291. def num_ephemeral_disks(self):
  1292. """
  1293. Python sdk does not return num_ephemeral_disks details.
  1294. In Azure, we cannot explicitly add ephemeral disks.
  1295. So, we are taking assumption and populating it as Zero.
  1296. """
  1297. return 0
  1298. @property
  1299. def extra_data(self):
  1300. return {
  1301. 'max_data_disk_count':
  1302. self._inst_type.max_data_disk_count
  1303. }