pyghmi 1.5.75__tar.gz → 1.5.77__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. {pyghmi-1.5.75 → pyghmi-1.5.77}/ChangeLog +13 -0
  2. {pyghmi-1.5.75 → pyghmi-1.5.77}/PKG-INFO +1 -1
  3. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/config.py +4 -0
  4. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/command.py +18 -2
  5. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/generic.py +19 -6
  6. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/xcc.py +1 -0
  7. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/xcc3.py +53 -0
  8. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/PKG-INFO +1 -1
  9. pyghmi-1.5.77/pyghmi.egg-info/pbr.json +1 -0
  10. pyghmi-1.5.75/pyghmi.egg-info/pbr.json +0 -1
  11. {pyghmi-1.5.75 → pyghmi-1.5.77}/.coveragerc +0 -0
  12. {pyghmi-1.5.75 → pyghmi-1.5.77}/.stestr.conf +0 -0
  13. {pyghmi-1.5.75 → pyghmi-1.5.77}/AUTHORS +0 -0
  14. {pyghmi-1.5.75 → pyghmi-1.5.77}/CONTRIBUTING.rst +0 -0
  15. {pyghmi-1.5.75 → pyghmi-1.5.77}/LICENSE +0 -0
  16. {pyghmi-1.5.75 → pyghmi-1.5.77}/MANIFEST.in +0 -0
  17. {pyghmi-1.5.75 → pyghmi-1.5.77}/README +0 -0
  18. {pyghmi-1.5.75 → pyghmi-1.5.77}/README.md +0 -0
  19. {pyghmi-1.5.75 → pyghmi-1.5.77}/builddeb +0 -0
  20. {pyghmi-1.5.75 → pyghmi-1.5.77}/buildrpm +0 -0
  21. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/requirements.txt +0 -0
  22. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/source/conf.py +0 -0
  23. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/source/contributor/index.rst +0 -0
  24. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/source/index.rst +0 -0
  25. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/source/install/index.rst +0 -0
  26. {pyghmi-1.5.75 → pyghmi-1.5.77}/doc/source/reference/index.rst +0 -0
  27. {pyghmi-1.5.75 → pyghmi-1.5.77}/makesetup +0 -0
  28. {pyghmi-1.5.75 → pyghmi-1.5.77}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/post.yaml +0 -0
  29. {pyghmi-1.5.75 → pyghmi-1.5.77}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/run.yaml +0 -0
  30. {pyghmi-1.5.75 → pyghmi-1.5.77}/py27-constraints.txt +0 -0
  31. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/__init__.py +0 -0
  32. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/cmd/__init__.py +0 -0
  33. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/cmd/fakebmc.py +0 -0
  34. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/cmd/pyghmicons.py +0 -0
  35. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/cmd/pyghmiutil.py +0 -0
  36. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/cmd/virshbmc.py +0 -0
  37. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/constants.py +0 -0
  38. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/exceptions.py +0 -0
  39. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/__init__.py +0 -0
  40. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/bmc.py +0 -0
  41. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/command.py +0 -0
  42. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/console.py +0 -0
  43. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/events.py +0 -0
  44. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/fru.py +0 -0
  45. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/__init__.py +0 -0
  46. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/generic.py +0 -0
  47. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/__init__.py +0 -0
  48. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/cpu.py +0 -0
  49. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/dimm.py +0 -0
  50. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/drive.py +0 -0
  51. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/energy.py +0 -0
  52. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/firmware.py +0 -0
  53. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/handler.py +0 -0
  54. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/imm.py +0 -0
  55. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/inventory.py +0 -0
  56. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/nextscale.py +0 -0
  57. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/pci.py +0 -0
  58. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/psu.py +0 -0
  59. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/raid_controller.py +0 -0
  60. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lenovo/raid_drive.py +0 -0
  61. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/oem/lookup.py +0 -0
  62. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/__init__.py +0 -0
  63. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/constants.py +0 -0
  64. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/localsession.py +0 -0
  65. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/serversession.py +0 -0
  66. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/session.py +0 -0
  67. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/simplesession.py +0 -0
  68. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/spd.py +0 -0
  69. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/private/util.py +0 -0
  70. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/ipmi/sdr.py +0 -0
  71. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/media.py +0 -0
  72. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/__init__.py +0 -0
  73. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/__init__.py +0 -0
  74. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/dell/__init__.py +0 -0
  75. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/dell/idrac.py +0 -0
  76. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/dell/main.py +0 -0
  77. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/__init__.py +0 -0
  78. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/main.py +0 -0
  79. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/smm3.py +0 -0
  80. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lenovo/tsma.py +0 -0
  81. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/redfish/oem/lookup.py +0 -0
  82. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/storage.py +0 -0
  83. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/tests/__init__.py +0 -0
  84. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/tests/unit/__init__.py +0 -0
  85. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/tests/unit/base.py +0 -0
  86. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/tests/unit/ipmi/__init__.py +0 -0
  87. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/tests/unit/ipmi/test_sdr.py +0 -0
  88. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/util/__init__.py +0 -0
  89. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/util/parse.py +0 -0
  90. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/util/webclient.py +0 -0
  91. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi/version.py +0 -0
  92. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/SOURCES.txt +0 -0
  93. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/dependency_links.txt +0 -0
  94. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/entry_points.txt +0 -0
  95. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/not-zip-safe +0 -0
  96. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/requires.txt +0 -0
  97. {pyghmi-1.5.75 → pyghmi-1.5.77}/pyghmi.egg-info/top_level.txt +0 -0
  98. {pyghmi-1.5.75 → pyghmi-1.5.77}/python-pyghmi.spec +0 -0
  99. {pyghmi-1.5.75 → pyghmi-1.5.77}/python-pyghmi.spec.tmpl +0 -0
  100. {pyghmi-1.5.75 → pyghmi-1.5.77}/requirements.txt +0 -0
  101. {pyghmi-1.5.75 → pyghmi-1.5.77}/setup.cfg +0 -0
  102. {pyghmi-1.5.75 → pyghmi-1.5.77}/setup.py +0 -0
  103. {pyghmi-1.5.75 → pyghmi-1.5.77}/setup.py.tmpl +0 -0
  104. {pyghmi-1.5.75 → pyghmi-1.5.77}/test-requirements.txt +0 -0
  105. {pyghmi-1.5.75 → pyghmi-1.5.77}/tox.ini +0 -0
  106. {pyghmi-1.5.75 → pyghmi-1.5.77}/wheezy.patch +0 -0
  107. {pyghmi-1.5.75 → pyghmi-1.5.77}/zuul.d/project.yaml +0 -0
@@ -1,6 +1,19 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ 1.5.77
5
+ ------
6
+
7
+ * Fix numeric indexs into configuration
8
+
9
+ 1.5.76
10
+ ------
11
+
12
+ * Handle redfish modeling of some IB adapters
13
+ * Add XCC3 specific health check
14
+ * General fixup for sensor handling
15
+ * Suspend use of token for remote media attach
16
+
4
17
  1.5.75
5
18
  ------
6
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyghmi
3
- Version: 1.5.75
3
+ Version: 1.5.77
4
4
  Summary: Python General Hardware Management Initiative (IPMI and others)
5
5
  Home-page: http://github.com/openstack/pyghmi/
6
6
  Author: Jarrod Johnson
@@ -351,6 +351,7 @@ class LenovoFirmwareConfig(object):
351
351
  readonly = 'true'
352
352
  possible = []
353
353
  current = None
354
+ currentidxes = []
354
355
  default = None
355
356
  reset = False
356
357
  name = setting.find("mriName").text
@@ -381,6 +382,7 @@ class LenovoFirmwareConfig(object):
381
382
  instbynum[xid] = x
382
383
  defidx += 1
383
384
  current = [instbynum[idx].text for idx in sorted(instbynum)]
385
+ currentidxes = list(sorted(instbynum))
384
386
  default = onedata.get('default', None)
385
387
  if default == '':
386
388
  default = None
@@ -442,6 +444,8 @@ class LenovoFirmwareConfig(object):
442
444
  if current and len(current) > 1:
443
445
  instidx = 1
444
446
  for inst in current:
447
+ if currentidxes:
448
+ instidx = currentidxes.pop(0)
445
449
  optname = '{0}.{1}'.format(optionname, instidx)
446
450
  options[optname] = dict(
447
451
  current=inst,
@@ -68,6 +68,7 @@ _healthmap = {
68
68
  'Unknown': const.Health.Warning,
69
69
  'Warning': const.Health.Warning,
70
70
  'OK': const.Health.Ok,
71
+ None: const.Health.Ok,
71
72
  }
72
73
 
73
74
 
@@ -123,7 +124,7 @@ class SensorReading(object):
123
124
  self.states = [healthinfo.get('Status', {}).get('Health',
124
125
  'Unknown')]
125
126
  self.health = _healthmap[healthinfo['Status']['Health']]
126
- if healthinfo['Status']['Health'].lower() == 'ok':
127
+ if self.health == const.Health.Ok:
127
128
  self.states = []
128
129
  self.value = value
129
130
  self.state_ids = None
@@ -692,7 +693,9 @@ class Command(object):
692
693
  def _mapchassissensors(self, chassis):
693
694
  chassisurl = chassis['@odata.id']
694
695
  chassisinfo = self._do_web_request(chassisurl)
695
- sensors = chassisinfo.get('Sensors', {}).get('@odata.id', '')
696
+ sensors = None
697
+ if self.oem.usegenericsensors:
698
+ sensors = chassisinfo.get('Sensors', {}).get('@odata.id', '')
696
699
  if sensors:
697
700
  sensorinf = self._do_web_request(sensors)
698
701
  for sensor in sensorinf.get('Members', []):
@@ -1379,9 +1382,18 @@ class Command(object):
1379
1382
  if vmcoll:
1380
1383
  vmlist = self._do_web_request(vmcoll)
1381
1384
  vmurls = [x['@odata.id'] for x in vmlist.get('Members', [])]
1385
+ suspendedxauth = False
1386
+ if 'X-Auth-Token' in self.wc.stdheaders:
1387
+ suspendedxauth = True
1388
+ del self.wc.stdheaders['X-Auth-Token']
1389
+ self.wc.set_basic_credentials(self.username, self.password)
1382
1390
  try:
1383
1391
  self.oem.attach_remote_media(url, username, password, vmurls)
1384
1392
  except exc.BypassGenericBehavior:
1393
+ if suspendedxauth:
1394
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
1395
+ if 'Authorization' in self.wc.stdheaders:
1396
+ del self.wc.stdheaders['Authorization']
1385
1397
  return
1386
1398
  for vmurl in vmurls:
1387
1399
  vminfo = self._do_web_request(vmurl, cache=False)
@@ -1403,6 +1415,10 @@ class Command(object):
1403
1415
  else:
1404
1416
  raise
1405
1417
  break
1418
+ if suspendedxauth:
1419
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
1420
+ if 'Authorization' in self.wc.stdheaders:
1421
+ del self.wc.stdheaders['Authorization']
1406
1422
 
1407
1423
  def detach_remote_media(self):
1408
1424
  try:
@@ -184,6 +184,7 @@ class AttrDependencyHandler(object):
184
184
 
185
185
  class OEMHandler(object):
186
186
  hostnic = None
187
+ usegenericsensors = True
187
188
 
188
189
  def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None):
189
190
  self._gpool = gpool
@@ -787,17 +788,29 @@ class OEMHandler(object):
787
788
  if 'Name' not in nadinfo:
788
789
  continue
789
790
  nicname = nadinfo['Name']
791
+ if nicname == 'NetworkAdapter':
792
+ nicname = nadinfo.get('Model', nicname)
790
793
  yieldinf = {}
791
794
  macidx = 1
792
795
  if 'Ports' in nadinfo:
793
796
  for portinfo in self._get_expanded_data(
794
797
  nadinfo['Ports']['@odata.id']).get('Members', []):
795
- macs = [x for x in portinfo.get('Ethernet', {}).get('AssociatedMACAddresses', [])]
796
- for mac in macs:
797
- label = 'MAC Address {}'.format(macidx)
798
- yieldinf[label] = _normalize_mac(mac)
799
- macidx += 1
800
- foundmacs = True
798
+ ethinfo = portinfo.get('Ethernet', {})
799
+ if ethinfo:
800
+ macs = [x for x in ethinfo.get('AssociatedMACAddresses', [])]
801
+ for mac in macs:
802
+ label = 'MAC Address {}'.format(macidx)
803
+ yieldinf[label] = _normalize_mac(mac)
804
+ macidx += 1
805
+ foundmacs = True
806
+ ibinfo = portinfo.get('InfiniBand', {})
807
+ if ibinfo:
808
+ macs = [x for x in ibinfo.get('AssociatedPortGUIDs', [])]
809
+ for mac in macs:
810
+ label = 'Port GUID {}'.format(macidx)
811
+ yieldinf[label] = mac
812
+ macidx += 1
813
+ foundmacs = True
801
814
  macinfobyadpname[nicname] = yieldinf
802
815
  else:
803
816
  for ctrlr in nadinfo.get('Controllers', []):
@@ -119,6 +119,7 @@ def str_to_size(sizestr):
119
119
 
120
120
 
121
121
  class OEMHandler(generic.OEMHandler):
122
+ usegenericsensors = False
122
123
  logouturl = '/api/providers/logout'
123
124
  bmcname = 'XCC'
124
125
  ADP_URL = '/api/dataset/imm_adapters?params=pci_GetAdapters'
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  import copy
15
15
  import json
16
+ import pyghmi.constants as pygconst
16
17
  import pyghmi.redfish.oem.generic as generic
17
18
  import pyghmi.exceptions as pygexc
18
19
  import pyghmi.util.webclient as webclient
@@ -20,6 +21,22 @@ import zipfile
20
21
  import time
21
22
  import os.path
22
23
 
24
+
25
+ class SensorReading(object):
26
+ def __init__(self, healthinfo, sensor=None, value=None, units=None,
27
+ unavailable=False):
28
+ if sensor:
29
+ self.name = sensor['name']
30
+ else:
31
+ self.name = healthinfo['name']
32
+ self.health = healthinfo['health']
33
+ self.states = healthinfo['states']
34
+ self.state_ids = healthinfo.get('state_ids', None)
35
+ self.value = value
36
+ self.imprecision = None
37
+ self.units = units
38
+ self.unavailable = unavailable
39
+
23
40
  class OEMHandler(generic.OEMHandler):
24
41
 
25
42
  def supports_expand(self, url):
@@ -64,6 +81,42 @@ class OEMHandler(generic.OEMHandler):
64
81
  powerinfo = fishclient._do_web_request('/redfish/v1/Chassis/1/Sensors/power_Sys_Power')
65
82
  return powerinfo['Reading']
66
83
 
84
+ def get_health(self, fishclient, verbose=True):
85
+ rsp = self._do_web_request('/api/providers/imm_active_events')
86
+ summary = {'badreadings': [], 'health': pygconst.Health.Ok}
87
+ fallbackdata = []
88
+ hmap = {
89
+ 0 : pygconst.Health.Ok,
90
+ 3: pygconst.Health.Critical,
91
+ 2: pygconst.Health.Warning,
92
+ }
93
+ infoevents = False
94
+ existingevts = set([])
95
+ for item in rsp.get('items', ()):
96
+ # while usually the ipmi interrogation shall explain things,
97
+ # just in case there is a gap, make sure at least the
98
+ # health field is accurately updated
99
+ itemseverity = hmap.get(item.get('Severity', 2),
100
+ pygconst.Health.Critical)
101
+ if itemseverity == pygconst.Health.Ok:
102
+ infoevents = True
103
+ continue
104
+ if (summary['health'] < itemseverity):
105
+ summary['health'] = itemseverity
106
+ evtsrc = item.get('Oem', {}).get('Lenovo', {}).get('Source', '')
107
+ currevt = '{}:{}'.format(evtsrc, item['Message'])
108
+ if currevt in existingevts:
109
+ continue
110
+ existingevts.add(currevt)
111
+ fallbackdata.append(SensorReading({
112
+ 'name': evtsrc,
113
+ 'states': [item['Message']],
114
+ 'health': itemseverity,
115
+ 'type': evtsrc,
116
+ }, ''))
117
+ summary['badreadings'] = fallbackdata
118
+ return summary
119
+
67
120
  def _get_cpu_temps(self, fishclient):
68
121
  cputemps = []
69
122
  for reading in super()._get_cpu_temps(fishclient):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyghmi
3
- Version: 1.5.75
3
+ Version: 1.5.77
4
4
  Summary: Python General Hardware Management Initiative (IPMI and others)
5
5
  Home-page: http://github.com/openstack/pyghmi/
6
6
  Author: Jarrod Johnson
@@ -0,0 +1 @@
1
+ {"git_version": "4bbf63c", "is_release": true}
@@ -1 +0,0 @@
1
- {"git_version": "8666417", "is_release": true}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes