A REST API for cloud embedded board reservation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

185 lines
5.3 KiB

  1. #
  2. # Copyright (c) 2020 The FreeBSD Foundation
  3. #
  4. # This software1 was developed by John-Mark Gurney under sponsorship
  5. # from the FreeBSD Foundation.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions
  9. # are met:
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in the
  14. # documentation and/or other materials provided with the distribution.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. # SUCH DAMAGE.
  27. #
  28. from unittest.mock import patch
  29. from .abstract import *
  30. from .mocks import *
  31. import asyncio
  32. import subprocess
  33. import unittest
  34. __all__ = [ 'snmpget', 'snmpset', 'SNMPPower' ]
  35. def _tosnmp(typ, value):
  36. if typ == 'bool':
  37. if value:
  38. outv = 'true'
  39. else:
  40. outv = 'false'
  41. return ('i', outv)
  42. raise RuntimeError('unknown type: %s' % repr(typ))
  43. def _fromsnmp(typ, value):
  44. if typ == 'bool':
  45. if value == b'true':
  46. return True
  47. elif value == b'false':
  48. return False
  49. raise RuntimeError('unknown results for bool: %s' % repr(value))
  50. raise RuntimeError('unknown type: %s' % repr(typ))
  51. async def snmpget(host, oid, typ):
  52. p = await asyncio.create_subprocess_exec('snmpget', '-Oqv', host, oid,
  53. stdout=subprocess.PIPE)
  54. res = (await p.communicate())[0].strip()
  55. return _fromsnmp(typ, res)
  56. async def snmpset(host, oid, typ, value):
  57. p = await asyncio.create_subprocess_exec('snmpset', '-Oqv', host, oid,
  58. *_tosnmp(typ, value), stdout=subprocess.PIPE)
  59. res = (await p.communicate())[0].strip()
  60. return _fromsnmp(typ, res)
  61. class SNMPPower(Power):
  62. def __init__(self, host, port):
  63. self.host = host
  64. self.port = port
  65. # Future - add caching + invalidation on set
  66. async def getvalue(self):
  67. return await snmpget(self.host,
  68. 'pethPsePortAdminEnable.1.%d' % self.port, 'bool')
  69. async def setvalue(self, v):
  70. return await snmpset(self.host,
  71. 'pethPsePortAdminEnable.1.%d' % self.port, 'bool', v)
  72. async def deactivate(self, brd):
  73. return await self.setvalue(False)
  74. class TestSNMPWrapper(unittest.IsolatedAsyncioTestCase):
  75. @patch('asyncio.create_subprocess_exec')
  76. async def test_snmpset(self, cse):
  77. # that when snmpset returns false
  78. wrap_subprocess_exec(cse, b'false\n')
  79. # when being set to false
  80. r = await snmpset('somehost', 'snmpoid', 'bool', False)
  81. # that it returns false
  82. self.assertEqual(r, False)
  83. # and is called with the correct parameters
  84. cse.assert_called_with('snmpset', '-Oqv', 'somehost',
  85. 'snmpoid', 'i', 'false', stdout=subprocess.PIPE)
  86. # that when snmpset returns true
  87. wrap_subprocess_exec(cse, b'true\n')
  88. # when being set to true
  89. r = await snmpset('somehost', 'snmpoid', 'bool', True)
  90. # that it returns true
  91. self.assertEqual(r, True)
  92. # and is called with the correct parameters
  93. cse.assert_called_with('snmpset', '-Oqv', 'somehost',
  94. 'snmpoid', 'i', 'true', stdout=subprocess.PIPE)
  95. async def test_snmpset_wrongtype(self):
  96. with self.assertRaises(RuntimeError):
  97. await snmpset('somehost', 'snmpoid', 'boi', None)
  98. @patch('asyncio.create_subprocess_exec')
  99. async def test_snmpget(self, cse):
  100. wrap_subprocess_exec(cse, b'false\n')
  101. r = await snmpget('somehost', 'snmpoid', 'bool')
  102. self.assertEqual(r, False)
  103. cse.assert_called_with('snmpget', '-Oqv', 'somehost',
  104. 'snmpoid', stdout=subprocess.PIPE)
  105. wrap_subprocess_exec(cse, b'true\n')
  106. r = await snmpget('somehost', 'snmpoid', 'bool')
  107. self.assertEqual(r, True)
  108. # that a bogus return value
  109. wrap_subprocess_exec(cse, b'bogus\n')
  110. # raises an error
  111. with self.assertRaises(RuntimeError):
  112. await snmpget('somehost', 'snmpoid', 'bool')
  113. # that an unknown type, raises an error
  114. with self.assertRaises(RuntimeError):
  115. await snmpget('somehost', 'snmpoid', 'randomtype')
  116. class TestSNMPPower(unittest.IsolatedAsyncioTestCase):
  117. @patch('bitelab.snmp.snmpset')
  118. @patch('bitelab.snmp.snmpget')
  119. async def test_snmppower(self, sg, ss):
  120. sp = SNMPPower('host', 5)
  121. # that when snmpget returns False
  122. sg.return_value = False
  123. self.assertFalse(await sp.getvalue())
  124. # calls snmpget w/ the correct args
  125. sg.assert_called_with('host', 'pethPsePortAdminEnable.1.5',
  126. 'bool')
  127. # that when setvalue is called
  128. await sp.setvalue(True)
  129. # calls snmpset w/ the correct args
  130. ss.assert_called_with('host', 'pethPsePortAdminEnable.1.5',
  131. 'bool', True)
  132. ss.reset_mock()
  133. # that when deactivate is called
  134. await sp.deactivate(None)
  135. # calls snmpset w/ the correct args
  136. ss.assert_called_with('host', 'pethPsePortAdminEnable.1.5',
  137. 'bool', False)