diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..2dcf407 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,139 @@ +name: CI +on: [push, pull_request] +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [7.4] + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + coverage: none + php-version: ${{ matrix.php-version }} + tools: cs2pr + extensions: json snmp + + - name: Cache dependencies installed with composer + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-version }} + restore-keys: php-${{ matrix.php-version }} + + - name: Install dependencies with composer + run: COMPOSER_ARGS="--prefer-stable" make + + - name: Run a static analysis with phpstan/phpstan + env: + PHPSTAN_ARGS: --error-format=checkstyle + run: make -is static-analysis | cs2pr + + coding-standards: + name: Coding Standards + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [7.3] + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + coverage: none + php-version: ${{ matrix.php-version }} + tools: cs2pr + + - name: Cache dependencies installed with composer + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-version }} + restore-keys: php-${{ matrix.php-version }} + + - name: Install dependencies with composer + run: COMPOSER_ARGS="--prefer-stable" make + + - name: Run squizlabs/php_codesniffer + env: + PHPCS_ARGS: -q --no-colors --report=checkstyle + run: make -is cs | cs2pr + + tests: + name: Tests + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [7.3, 7.4] + dependencies: ["", --prefer-lowest] + + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + coverage: none + php-version: ${{ matrix.php-version }} + extensions: json snmp + + - name: Install dependencies + run: sudo apt install python-pip snmp + + - name: Install snmpsim + run: sudo pip install snmpsim + + - name: Cache dependencies installed with composer + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-version }}-dependencies-${{ matrix.dependencies }} + restore-keys: php-${{ matrix.php-version }} + + - name: Install dependencies with composer + run: COMPOSER_ARGS="--prefer-stable ${{ matrix.dependencies }}" make + + - name: version + run: php -v && php -i + + - name: Run tests + run: make test + + coverage: + name: Tests Coverage + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [7.4] + + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v2 + with: + coverage: pcov + php-version: ${{ matrix.php-version }} + extensions: json snmp + + - name: Install dependencies + run: sudo apt install python-pip snmp + + - name: Install snmpsim + run: sudo pip install snmpsim + + - name: Cache dependencies installed with composer + uses: actions/cache@v1 + with: + path: ~/.composer/cache + key: php-${{ matrix.php-version }} + restore-keys: php-${{ matrix.php-version }} + + - name: Install dependencies with composer + run: COMPOSER_ARGS="--prefer-stable" make + + - name: Run tests coverage + run: PHPUNIT_ARGS="--coverage-clover coverage/clover.xml" make test + + - name: Report to Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_RUN_LOCALLY: 1 + run: pwd && ls -la && ls -la coverage && echo $USER && vendor/bin/php-coveralls --verbose --coverage_clover coverage/clover.xml --json_path coverage diff --git a/.gitignore b/.gitignore index 8da4a33..d896cee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ +/coverage/ /vendor/ + /.phpcs-cache +/.phpunit.result.cache /composer.lock /phpcs.xml /phpstan.neon +/phpunit.xml diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index dae628f..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,17 +0,0 @@ -build: - environment: - elasticsearch: false - memcached: false - mongodb: false - mysql: false - neo4j: false - postgresql: false - rabbitmq: false - redis: false - -tools: - external_code_coverage: true - -checks: - php: - code_rating: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b06e1e7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,38 +0,0 @@ -dist: trusty -language: php -sudo: required - -php: - - 7.2 - - 7.3 - - nightly - -before_install: - - echo "extension = snmp.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - composer self-update - -before_script: - - travis_retry composer update --prefer-dist - -script: ./vendor/bin/phpunit - -jobs: - include: - - stage: Coding Standard - php: 7.2 - script: ./vendor/bin/phpcs - - - stage: Static Analysis - php: 7.2 - script: - - ./vendor/bin/phpstan analyse -c phpstan.neon.dist -l max src - -cache: - directories: - - $HOME/.composer/cache - -after_script: - # upload clover.xml file to Scrutinizer to analyze it - - | - if [ "$TRAVIS_PHP_VERSION" == "7.2" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - if [ "$TRAVIS_PHP_VERSION" == "7.2" ]; then php ocular.phar code-coverage:upload --format=php-clover temp/clover.xml; fi diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7d10895 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +COMPOSER_ARGS += --no-interaction --no-progress --no-suggest + +.PHONY: build +build: vendor + +.PHONY: vendor +vendor: vendor/lock + +vendor/lock: composer.json + composer update $(COMPOSER_ARGS) + touch vendor/lock + +.PHONY: test +test: + vendor/bin/phpunit $(PHPUNIT_ARGS) + +.PHONY: cs +cs: + vendor/bin/phpcs $(PHPCS_ARGS) + +.PHONY: fix +fix: + vendor/bin/phpcbf + +.PHONY: static-analysis +static-analysis: + vendor/bin/phpstan analyse $(PHPSTAN_ARGS) + +.PHONY: check +check: build cs static-analysis test + +.PHONY: clean +clean: clean-vendor + +.PHONY: clean-vendor +clean-vendor: + rm -rf vendor diff --git a/README.md b/README.md new file mode 100644 index 0000000..0851ddb --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +simpod/php-snmp [![Coverage Status](https://coveralls.io/repos/github/simPod/PHP-SNMP/badge.svg)](https://coveralls.io/github/simPod/PHP-SNMP) +=============== diff --git a/composer.json b/composer.json index 1a280f9..391d671 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,16 @@ { "name": "simpod/php-snmp", - "description": "PHP Simple Network Management", + "description": "PHP library for connecting to the SNMP agents; includes small collection of OIDs, neatly packed into classes/methods for convenient use.", "type": "library", "license": "MIT", "authors": [ { - "name": "Simon Podlipsky", + "name": "Šimon Podlipský", "email": "simon@podlipsky.net" + }, + { + "name": "Jakub Chábek", + "email": "jakub@chabek.cz" } ], "keywords": [ @@ -17,14 +21,36 @@ "sort-packages": true }, "require": { - "php": "^7.2", - "ext-snmp": "*", - "consistence/consistence": "^1.0|^2.0" + "php": "^7.3|^8.0" + }, + "suggest": { + "ext-json": "Required for SNMP transport \"Api\"", + "ext-snmp": "Required for SNMP transport \"Extension\"", + "symfony/process": "Required for default implementation of SNMP transport \"Cli\"", + "nyholm/psr7": "Required for SNMP transport \"Api\"", + "psr/http-client": "Required for SNMP transport \"Api\"", + "psr/http-factory": "Required for SNMP transport \"Api\"", + "psr/http-message": "Required for SNMP transport \"Api\"" }, "require-dev": { + "ext-json": "*", + "ext-snmp": "*", "cdn77/coding-standard": "^3.0", - "phpstan/phpstan": "^0.11.5", - "phpstan/phpstan-strict-rules": "^0.11.0" + "nyholm/psr7": "^1.3", + "php-http/curl-client": "^2.1", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.25", + "phpstan/phpstan-deprecation-rules": "^0.12.3", + "phpstan/phpstan-phpunit": "^0.12.8", + "phpstan/phpstan-strict-rules": "^0.12.2", + "phpunit/phpunit": "^9.1", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "simpod/php-coveralls-mirror": "^3.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.3", + "thecodingmachine/phpstan-safe-rule": "^1.0" }, "autoload": { "psr-4": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index eeea86b..7374d1c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,11 +1,9 @@ parameters: - memory-limit: -1 level: max paths: - %currentWorkingDirectory%/src - %currentWorkingDirectory%/tests - -includes: - - vendor/phpstan/phpstan-phpunit/extension.neon - - vendor/phpstan/phpstan-phpunit/rules.neon - - vendor/phpstan/phpstan-strict-rules/rules.neon + ignoreErrors: + # extension is badly designed, we know these types won't ever be here + - '~Parameter #1 \$output of method SimPod\\PhpSnmp\\Transport\\ExtensionSnmpClient::processOutput\(\) expects array, array\|string\|false given\.~' + - '~Parameter #1 \$output of method SimPod\\PhpSnmp\\Transport\\ExtensionSnmpClient::processOutput\(\) expects array, array\|false given\.~' diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 50% rename from phpunit.xml rename to phpunit.xml.dist index d50bf82..2e1c3b5 100644 --- a/phpunit.xml +++ b/phpunit.xml.dist @@ -1,11 +1,10 @@ - - @@ -15,18 +14,8 @@ - + src - - - - - diff --git a/src/Exception/CannotParseUnknownValueType.php b/src/Exception/CannotParseUnknownValueType.php new file mode 100644 index 0000000..177805b --- /dev/null +++ b/src/Exception/CannotParseUnknownValueType.php @@ -0,0 +1,16 @@ +getMessage(), $matches) !== 1) { + throw self::new(); + } + + return self::withOid($matches[1]); + } +} diff --git a/src/Exception/GeneralException.php b/src/Exception/GeneralException.php new file mode 100644 index 0000000..e2e3a86 --- /dev/null +++ b/src/Exception/GeneralException.php @@ -0,0 +1,18 @@ +getMessage(), $matches) !== 1) { + throw self::new(); + } + + return self::withOid($matches[1]); + } +} diff --git a/src/Exception/NoSuchObjectExists.php b/src/Exception/NoSuchObjectExists.php new file mode 100644 index 0000000..b95cc38 --- /dev/null +++ b/src/Exception/NoSuchObjectExists.php @@ -0,0 +1,37 @@ +getMessage(), $matches) !== 1) { + throw self::new(); + } + + return self::withOid($matches[1]); + } +} diff --git a/src/SnmpException.php b/src/Exception/SnmpException.php similarity index 72% rename from src/SnmpException.php rename to src/Exception/SnmpException.php index 9b8f1a4..406cfab 100644 --- a/src/SnmpException.php +++ b/src/Exception/SnmpException.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimPod\PhpSnmp; +namespace SimPod\PhpSnmp\Exception; use Throwable; diff --git a/src/Exception/SnmpFailed.php b/src/Exception/SnmpFailed.php deleted file mode 100644 index 4151683..0000000 --- a/src/Exception/SnmpFailed.php +++ /dev/null @@ -1,12 +0,0 @@ -getKey(), (float) $keyValuePair->getValue()); - } - ); - } -} diff --git a/src/Helpers/OidStripper.php b/src/Helpers/OidStripper.php new file mode 100644 index 0000000..365cb18 --- /dev/null +++ b/src/Helpers/OidStripper.php @@ -0,0 +1,52 @@ + $raw + * + * @return array + */ + public static function stripLeafOidsParentOid(array $raw) : array + { + $firstKey = array_key_first($raw); + assert(is_string($firstKey)); + + $lastDotPos = strrpos($firstKey, '.'); + assert($lastDotPos !== false); + + $stripLength = $lastDotPos + 1; + + $result = []; + foreach ($raw as $oid => $value) { + $result[(int) substr($oid, $stripLength)] = $value; + } + + return $result; + } + + /** @return array */ + public static function walk(SnmpClient $snmpClient, string $oid) : array + { + $stripLength = strlen($oid) + 1; + + $result = []; + foreach ($snmpClient->walk($oid) as $childOid => $value) { + $result[substr($childOid, $stripLength)] = $value; + } + + return $result; + } +} diff --git a/src/Helpers/SingleValue.php b/src/Helpers/SingleValue.php new file mode 100644 index 0000000..74a9f1d --- /dev/null +++ b/src/Helpers/SingleValue.php @@ -0,0 +1,20 @@ + $raw + * + * @return mixed + */ + public static function get(array $raw) + { + return array_shift($raw); + } +} diff --git a/src/Mib/Arista/EntitySensor.php b/src/Mib/Arista/EntitySensor.php index c96760b..e731b13 100644 --- a/src/Mib/Arista/EntitySensor.php +++ b/src/Mib/Arista/EntitySensor.php @@ -4,36 +4,10 @@ namespace SimPod\PhpSnmp\Mib\Arista; -use SimPod\PhpSnmp\Mib\MibBase; - -class EntitySensor extends MibBase +final class EntitySensor { public const OID_THRESHOLD_LOW_WARNING = '.1.3.6.1.4.1.30065.3.12.1.1.1.1'; public const OID_THRESHOLD_LOW_CRITICAL = '.1.3.6.1.4.1.30065.3.12.1.1.1.2'; public const OID_THRESHOLD_HIGH_WARNING = '.1.3.6.1.4.1.30065.3.12.1.1.1.3'; public const OID_THRESHOLD_HIGH_CRITICAL = '.1.3.6.1.4.1.30065.3.12.1.1.1.4'; - - /** @return int[] */ - public function getThresholdLowWarning() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_THRESHOLD_LOW_WARNING); - } - - /** @return int[] */ - public function getThresholdLowCritical() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_THRESHOLD_LOW_CRITICAL); - } - - /** @return int[] */ - public function getThresholdHighWarning() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_THRESHOLD_HIGH_WARNING); - } - - /** @return int[] */ - public function getThresholdHighCritical() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_THRESHOLD_HIGH_CRITICAL); - } } diff --git a/src/Mib/Cisco/EntitySensor.php b/src/Mib/Cisco/EntitySensor.php index 0d4b189..6c7f8e4 100644 --- a/src/Mib/Cisco/EntitySensor.php +++ b/src/Mib/Cisco/EntitySensor.php @@ -4,12 +4,10 @@ namespace SimPod\PhpSnmp\Mib\Cisco; -use SimPod\PhpSnmp\Mib\MibBase; - /** * See CISCO-ENTITY-SENSOR-MIB */ -class EntitySensor extends MibBase +final class EntitySensor { public const OID_PHYSICAL_SENSOR_TYPE = '.1.3.6.1.4.1.9.9.91.1.1.1.1.1'; public const OID_PHYSICAL_SENSOR_SCALE = '.1.3.6.1.4.1.9.9.91.1.1.1.1.2'; @@ -19,52 +17,4 @@ class EntitySensor extends MibBase public const OID_PHYSICAL_SENSOR_UNITS_DISPLAY = '.1.3.6.1.4.1.9.9.91.1.1.1.1.6'; public const OID_PHYSICAL_SENSOR_VALUE_TIME_STAMP = '.1.3.6.1.4.1.9.9.91.1.1.1.1.7'; public const OID_PHYSICAL_SENSOR_VALUE_UPDATE_RATE = '.1.3.6.1.4.1.9.9.91.1.1.1.1.8'; - - /** @return int[] */ - public function getPhysicalSensorType() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_TYPE); - } - - /** @return int[] */ - public function getPhysicalSensorScale() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_SCALE); - } - - /** @return int[] */ - public function getPhysicalSensorPrecision() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_PRECISION); - } - - /** @return int[] */ - public function getPhysicalSensorValue() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE); - } - - /** @return int[] */ - public function getPhysicalSensorOperStatus() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_OPER_STATUS); - } - - /** @return string[] */ - public function getPhysicalSensorUnitsDisplay() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_UNITS_DISPLAY); - } - - /** @return int[] */ - public function getPhysicalSensorValueTimeStamp() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE_TIME_STAMP); - } - - /** @return int[] */ - public function getPhysicalSensorValueUpdateRate() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE_UPDATE_RATE); - } } diff --git a/src/Mib/Cisco/EnvMon.php b/src/Mib/Cisco/EnvMon.php index 36673ba..601d6a9 100644 --- a/src/Mib/Cisco/EnvMon.php +++ b/src/Mib/Cisco/EnvMon.php @@ -4,39 +4,13 @@ namespace SimPod\PhpSnmp\Mib\Cisco; -use SimPod\PhpSnmp\Mib\MibBase; - /** * See CISCO-ENVMON-MIB */ -class EnvMon extends MibBase +final class EnvMon { public const OID_CISCO_ENV_MON_FAN_STATUS_DESRC = '1.3.6.1.4.1.9.9.13.1.4.1.2'; public const OID_CISCO_ENV_MON_FAN_STATE = '1.3.6.1.4.1.9.9.13.1.4.1.3'; public const OID_CISCO_ENV_MON_SUPPLY_STATUS_DESC = '1.3.6.1.4.1.9.9.13.1.5.1.2'; public const OID_CISCO_ENV_MON_SUPPLY_STATE = '1.3.6.1.4.1.9.9.13.1.5.1.3'; - - /** @return string[] */ - public function getCiscoEnvMonFanStatusDescr() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_CISCO_ENV_MON_FAN_STATUS_DESRC); - } - - /** @return int[] */ - public function getCiscoEnvMonFanState() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_CISCO_ENV_MON_FAN_STATE); - } - - /** @return string[] */ - public function getCiscoEnvMonSupplyStatusDescr() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_CISCO_ENV_MON_SUPPLY_STATUS_DESC); - } - - /** @return int[] */ - public function getCiscoEnvMonSupplyState() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_CISCO_ENV_MON_SUPPLY_STATE); - } } diff --git a/src/Mib/Coriant/Groove.php b/src/Mib/Coriant/Groove.php index dc712f1..5de31f5 100644 --- a/src/Mib/Coriant/Groove.php +++ b/src/Mib/Coriant/Groove.php @@ -4,13 +4,10 @@ namespace SimPod\PhpSnmp\Mib\Coriant; -use SimPod\PhpSnmp\Helper\TypeMapper; -use SimPod\PhpSnmp\Mib\MibBase; - /** * See Groove G30 release 2.1.0 https://mibs.observium.org/mib/CORIANT-GROOVE-MIB/ */ -class Groove extends MibBase +final class Groove { public const OID_SYSTEM_POWER_CONSUMPTION_CURRENT = '.1.3.6.1.4.1.42229.1.2.2.2.2'; public const OID_CARD_ADMIN_STATUS = '.1.3.6.1.4.1.42229.1.2.3.3.1.1.3'; @@ -38,160 +35,4 @@ class Groove extends MibBase public const OID_INVENTORY_SERIAL_NUMBER = '.1.3.6.1.4.1.42229.1.2.3.12.1.1.9'; public const OID_INVENTORY_FW_VERSION = '.1.3.6.1.4.1.42229.1.2.3.12.1.1.10'; public const OID_INVENTORY_PART_VERSION = '.1.3.6.1.4.1.42229.1.2.3.12.1.1.11'; - - /** @return float[] */ - public function getSystemPowerConsumptionCurrent() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_SYSTEM_POWER_CONSUMPTION_CURRENT)); - } - - /** @return int[] */ - public function getCardAdminStatus() : array - { - return $this->getSnmp()->walk(self::OID_CARD_ADMIN_STATUS); - } - - /** @return int[] */ - public function getCardOperStatus() : array - { - return $this->getSnmp()->walk(self::OID_CARD_OPER_STATUS); - } - - /** @return int[] */ - public function getCardFanSpeedRate() : array - { - return $this->getSnmp()->walk(self::OID_CARD_FAN_SPEED_RATE); - } - - /** @return float[] */ - public function getCardTemperature() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_CARD_TEMPERATURE)); - } - - /** @return string[] */ - public function getSubcardEquipmentNames() : array - { - return $this->getSnmp()->walk(self::OID_SUBCARD_EQUIPMENT_NAME); - } - - /** @return float[] */ - public function getPortRxOpticalPower() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_RX_OPTICAL_POWER)); - } - - /** @return float[] */ - public function getPortTxOpticalPower() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_TX_OPTICAL_POWER)); - } - - /** @return float[] */ - public function getPortRxOpticalPowerLane1() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_RX_OPTICAL_POWER_LANE_1)); - } - - /** @return float[] */ - public function getPortRxOpticalPowerLane2() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_RX_OPTICAL_POWER_LANE_2)); - } - - /** @return float[] */ - public function getPortRxOpticalPowerLane3() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_RX_OPTICAL_POWER_LANE_3)); - } - - /** @return float[] */ - public function getPortRxOpticalPowerLane4() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_RX_OPTICAL_POWER_LANE_4)); - } - - /** @return float[] */ - public function getPortTxOpticalPowerLane1() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_TX_OPTICAL_POWER_LANE_1)); - } - - /** @return float[] */ - public function getPortTxOpticalPowerLane2() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_TX_OPTICAL_POWER_LANE_2)); - } - - /** @return float[] */ - public function getPortTxOpticalPowerLane3() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_TX_OPTICAL_POWER_LANE_3)); - } - - /** @return float[] */ - public function getPortTxOpticalPowerLane4() : array - { - return TypeMapper::stringsToFloats($this->getSnmp()->walk(self::OID_PORT_TX_OPTICAL_POWER_LANE_4)); - } - - /** @return string[] */ - public function getPortNames() : array - { - return $this->getSnmp()->walk(self::OID_PORT_NAME); - } - - /** @return int[] */ - public function getPortAdminStatuses() : array - { - return $this->getSnmp()->walk(self::OID_PORT_ADMIN_STATUS); - } - - /** @return int[] */ - public function getPortOperStatuses() : array - { - return $this->getSnmp()->walk(self::OID_PORT_OPER_STATUS); - } - - /** @return string[] */ - public function getPortAliasNames() : array - { - return $this->getSnmp()->walk(self::OID_PORT_ALIAS_NAME); - } - - /** @return int[] */ - public function getInventoryEquipmentType() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_EQUIPMENT_TYPE); - } - - /** @return string[] */ - public function getInventoryModuleType() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_MODULE_TYPE); - } - - /** @return string[] */ - public function getInventorySerialNumber() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_SERIAL_NUMBER); - } - - /** @return string[] */ - public function getInventoryFwVersion() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_FW_VERSION); - } - - /** @return string[] */ - public function getInventoryVendor() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_VENDOR); - } - - /** @return string[] */ - public function getInventoryPartVersion() : array - { - return $this->getSnmp()->walk(self::OID_INVENTORY_PART_VERSION); - } } diff --git a/src/Mib/Dcp/DcpAlarm.php b/src/Mib/Dcp/DcpAlarm.php index ea518ec..e034ef9 100644 --- a/src/Mib/Dcp/DcpAlarm.php +++ b/src/Mib/Dcp/DcpAlarm.php @@ -4,12 +4,10 @@ namespace SimPod\PhpSnmp\Mib\Dcp; -use SimPod\PhpSnmp\Mib\MibBase; - /** * See iso(1).org(3).dod(6).internet(1).private(4).enterprise(1).smartoptics(30826).dcp(2).dcpGeneric(2).dcpAlarm(1) */ -class DcpAlarm extends MibBase +final class DcpAlarm { public const OID_DCP_ALARM_LOG_LIST_INDEX = '.1.3.6.1.4.1.30826.2.2.2.2.2.1.1'; public const OID_DCP_ALARM_LOG_LIST_LOCATION = '.1.3.6.1.4.1.30826.2.2.2.2.2.1.2'; @@ -19,10 +17,4 @@ class DcpAlarm extends MibBase public const OID_DCP_ALARM_LOG_LIST_START_TIME = '.1.3.6.1.4.1.30826.2.2.2.2.2.1.6'; public const OID_DCP_ALARM_LOG_LIST_END_TIME = '.1.3.6.1.4.1.30826.2.2.2.2.2.1.7'; public const OID_DCP_ALARM_LOG_LIST_SEQ_NUMBER = '.1.3.6.1.4.1.30826.2.2.2.2.2.1.8'; - - /** @return array */ - public function getDcpAlarmLogListLocation() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_ALARM_LOG_LIST_LOCATION); - } } diff --git a/src/Mib/Dcp/DcpInterface.php b/src/Mib/Dcp/DcpInterface.php index 178ee03..d869186 100644 --- a/src/Mib/Dcp/DcpInterface.php +++ b/src/Mib/Dcp/DcpInterface.php @@ -4,12 +4,10 @@ namespace SimPod\PhpSnmp\Mib\Dcp; -use SimPod\PhpSnmp\Mib\MibBase; - /** * See iso(1).org(3).dod(6).internet(1).private(4).enterprise(1).smartoptics(30826).dcp(2).dcpGeneric(2).dcpInterface(1) */ -class DcpInterface extends MibBase +final class DcpInterface { public const OID_DCP_INTERFACE_INDEX = '.1.3.6.1.4.1.30826.2.2.1.1.1.1.1'; public const OID_DCP_INTERFACE_NAME = '.1.3.6.1.4.1.30826.2.2.1.1.1.1.2'; @@ -20,62 +18,4 @@ class DcpInterface extends MibBase public const OID_DCP_INTERFACE_FORMAT = '.1.3.6.1.4.1.30826.2.2.1.1.1.1.7'; public const OID_DCP_INTERFACE_WAVELENGTH = '.1.3.6.1.4.1.30826.2.2.1.1.1.1.8'; public const OID_DCP_INTERFACE_CHANNEL_ID = '.1.3.6.1.4.1.30826.2.2.1.1.1.1.9'; - - /** @return array */ - public function getDcpInterfaceIndex() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_INDEX); - } - - /** @return array */ - public function getDcpInterfaceName() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_NAME); - } - - /** @return array */ - public function getDcpInterfaceRxPower() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_RX_POWER); - } - - /** @return array */ - public function getDcpInterfaceTxPower() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_TX_POWER); - } - - /** - * The operational state for the interface. idle(1), down(2), up(3) - * - * @return array - */ - public function getDcpInterfaceStatus() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_STATUS); - } - - /** @return array */ - public function getDcpInterfaceAlarm() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_ALARM); - } - - /** @return array */ - public function getDcpInterfaceFormat() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_FORMAT); - } - - /** @return array */ - public function getDcpInterfaceWavelength() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_WAVELENGTH); - } - - /** @return array */ - public function getDcpInterfaceChannelId() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DCP_INTERFACE_CHANNEL_ID); - } } diff --git a/src/Mib/DismanEvent.php b/src/Mib/DismanEvent.php deleted file mode 100644 index 70c9816..0000000 --- a/src/Mib/DismanEvent.php +++ /dev/null @@ -1,19 +0,0 @@ -getSnmp()->walkFirstDegree(self::OID_SYS_UP_TIME_INSTANCE); - } -} diff --git a/src/Mib/Entity.php b/src/Mib/Entity.php index c82bf10..0c359b0 100644 --- a/src/Mib/Entity.php +++ b/src/Mib/Entity.php @@ -7,7 +7,7 @@ /** * See RFC 4133 https://tools.ietf.org/html/rfc4133 */ -class Entity extends MibBase +final class Entity { public const OID_PHYSICAL_DESCRIPTION = '.1.3.6.1.2.1.47.1.1.1.1.2'; public const OID_PHYSICAL_VENDOR_TYPE = '.1.3.6.1.2.1.47.1.1.1.1.3'; @@ -18,77 +18,10 @@ class Entity extends MibBase public const OID_PHYSICAL_HARDWARE_REV = '.1.3.6.1.2.1.47.1.1.1.1.8'; public const OID_PHYSICAL_FIRMWARE_REV = '.1.3.6.1.2.1.47.1.1.1.1.9'; public const OID_PHYSICAL_SOFTWARE_REV = '.1.3.6.1.2.1.47.1.1.1.1.10'; - public const OID_PHYSICAL_SERIALNUM = '.1.3.6.1.2.1.47.1.1.1.1.11'; public const OID_PHYSICAL_SERIAL_NUM = '.1.3.6.1.2.1.47.1.1.1.1.11'; public const OID_PHYSICAL_MFG_NAME = '.1.3.6.1.2.1.47.1.1.1.1.12'; public const OID_PHYSICAL_MODEL_NAME = '.1.3.6.1.2.1.47.1.1.1.1.13'; public const OID_PHYSICAL_ALIAS = '.1.3.6.1.2.1.47.1.1.1.1.14'; public const OID_PHYSICAL_ASSET_ID = '.1.3.6.1.2.1.47.1.1.1.1.15'; public const OID_PHYSICAL_IS_FRU = '.1.3.6.1.2.1.47.1.1.1.1.16'; - - /** @return string[] */ - public function getPhysicalDescription() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_DESCRIPTION); - } - - /** @return string[] */ - public function getPhysicalVendorType() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_VENDOR_TYPE); - } - - /** @return int[] */ - public function getPhysicalContainedIn() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_CONTAINED_IN); - } - - /** @return int[] */ - public function getPhysicalClass() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_CLASS); - } - - /** @return string[] */ - public function getPhysicalName() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_NAME); - } - - /** @return string[] */ - public function getPhysicalHardwareRev() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_HARDWARE_REV); - } - - /** @return string[] */ - public function getPhysicalFirmwareRev() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_FIRMWARE_REV); - } - - /** @return string[] */ - public function getPhysicalSoftwareRev() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SOFTWARE_REV); - } - - /** @return string[] */ - public function getPhysicalSerialNumber() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SERIAL_NUM); - } - - /** @return string[] */ - public function getPhysicalMfgName() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_MFG_NAME); - } - - /** @return string[] */ - public function getPhysicalModelName() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_MODEL_NAME); - } } diff --git a/src/Mib/EntitySensor.php b/src/Mib/EntitySensor.php index f97dadf..a3c23e8 100644 --- a/src/Mib/EntitySensor.php +++ b/src/Mib/EntitySensor.php @@ -7,7 +7,7 @@ /** * See RFC 3433 https://tools.ietf.org/html/rfc3433 */ -class EntitySensor extends MibBase +final class EntitySensor { public const OID_PHYSICAL_SENSOR_TYPE = '.1.3.6.1.2.1.99.1.1.1.1'; public const OID_PHYSICAL_SENSOR_SCALE = '.1.3.6.1.2.1.99.1.1.1.2'; @@ -17,52 +17,4 @@ class EntitySensor extends MibBase public const OID_PHYSICAL_SENSOR_UNITS_DISPLAY = '.1.3.6.1.2.1.99.1.1.1.6'; public const OID_PHYSICAL_SENSOR_VALUE_TIME_STAMP = '.1.3.6.1.2.1.99.1.1.1.7'; public const OID_PHYSICAL_SENSOR_VALUE_UPDATE_RATE = '.1.3.6.1.2.1.99.1.1.1.8'; - - /** @return int[] */ - public function getPhysicalSensorType() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_TYPE); - } - - /** @return int[] */ - public function getPhysicalSensorScale() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_SCALE); - } - - /** @return int[] */ - public function getPhysicalSensorPrecision() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_PRECISION); - } - - /** @return int[] */ - public function getPhysicalSensorValue() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE); - } - - /** @return int[] */ - public function getPhysicalSensorOperStatus() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_OPER_STATUS); - } - - /** @return string[] */ - public function getPhysicalSensorUnitsDisplay() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_UNITS_DISPLAY); - } - - /** @return int[] */ - public function getPhysicalSensorValueTimeStamp() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE_TIME_STAMP); - } - - /** @return int[] */ - public function getPhysicalSensorValueUpdateRate() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_PHYSICAL_SENSOR_VALUE_UPDATE_RATE); - } } diff --git a/src/Mib/EntityState.php b/src/Mib/EntityState.php index 78a3791..63552e9 100644 --- a/src/Mib/EntityState.php +++ b/src/Mib/EntityState.php @@ -7,20 +7,8 @@ /** * See RFC 4268 https://tools.ietf.org/html/rfc4268 */ -class EntityState extends MibBase +final class EntityState { public const OID_ENT_STATE_ADMIN = '.1.3.6.1.2.1.131.1.1.1.2'; public const OID_ENT_STATE_OPER = '.1.3.6.1.2.1.131.1.1.1.3'; - - /** @return int[] */ - public function getEntStateAdmin() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_ENT_STATE_ADMIN); - } - - /** @return int[] */ - public function getEntStateOper() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_ENT_STATE_OPER); - } } diff --git a/src/Mib/HostResources.php b/src/Mib/HostResources.php index b282731..33bbe35 100644 --- a/src/Mib/HostResources.php +++ b/src/Mib/HostResources.php @@ -7,7 +7,7 @@ /** * See RFC 2790 https://tools.ietf.org/html/rfc2790 */ -class HostResources extends MibBase +final class HostResources { public const OID_HOST = '.1.3.6.1.2.1.25'; public const OID_HR_SYSTEM = '.1.3.6.1.2.1.25.1'; @@ -113,58 +113,4 @@ class HostResources extends MibBase public const OID_HR_SWRUN_GROUP = '.1.3.6.1.2.1.25.7.3.4'; public const OID_HR_SWRUN_PERF_GROUP = '.1.3.6.1.2.1.25.7.3.5'; public const OID_HR_SWINSTALLED_GROUP = '.1.3.6.1.2.1.25.7.3.6'; - - /** @return int[] */ - public function getHost() : array - { - return $this->getSnmp()->walk(self::OID_HOST); - } - - /** @return string[] */ - public function getHrStorageType() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_STORAGE_TYPE); - } - - /** @return string[] */ - public function getHrStorageDescr() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_STORAGE_DESCR); - } - - /** @return int[] */ - public function getHrStorageSize() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_STORAGE_SIZE); - } - - /** @return int[] */ - public function getHrStorageUsed() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_STORAGE_USED); - } - - /** @return int[] */ - public function getHrDeviceType() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_DEVICE_TYPE); - } - - /** @return string[] */ - public function getHrDeviceDescr() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_DEVICE_DESCR); - } - - /** @return int[] */ - public function getHrDeviceStatus() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_DEVICE_STATUS); - } - - /** @return int[] */ - public function getHrProcessorLoad() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HR_PROCESSOR_LOAD); - } } diff --git a/src/Mib/Iface.php b/src/Mib/Iface.php index dd1d479..d404fe8 100644 --- a/src/Mib/Iface.php +++ b/src/Mib/Iface.php @@ -4,9 +4,7 @@ namespace SimPod\PhpSnmp\Mib; -use SimPod\PhpSnmp\OidWithIndex; - -class Iface extends MibBase +final class Iface { public const OID_ADMIN_STATUS = '.1.3.6.1.2.1.2.2.1.7'; public const OID_ALIAS = '.1.3.6.1.2.1.31.1.1.1.18'; @@ -29,208 +27,4 @@ class Iface extends MibBase public const OID_IN_DISCARDS = '.1.3.6.1.2.1.2.2.1.13'; public const OID_OUT_DISCARDS = '.1.3.6.1.2.1.2.2.1.19'; public const OID_STACK_STATUS = '.1.3.6.1.2.1.31.1.2.1.3'; - - /** @return int[] */ - public function getAdminStatuses() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_ADMIN_STATUS); - } - - /** @return string[] */ - public function getAliases() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_ALIAS); - } - - /** @return string[] */ - public function getDescriptions() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_DESCRIPTION); - } - - /** @return string[] */ - public function getHcInOctets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_IN_OCTETS); - } - - public function getHcInOctetsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_HC_IN_OCTETS, $index))[$index]; - } - - /** @return string[] */ - public function getHcOutOctets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_OUT_OCTETS); - } - - public function getHcOutOctetsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_HC_OUT_OCTETS, $index))[$index]; - } - - /** @return string[] */ - public function getHcInBroadcastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_IN_BROADCAST_PACKETS); - } - - public function getHcInBroadcastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_IN_BROADCAST_PACKETS, - $index - ))[$index]; - } - - /** @return string[] */ - public function getHcOutBroadcastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_OUT_BROADCAST_PACKETS); - } - - public function getHcOutBroadcastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_OUT_BROADCAST_PACKETS, - $index - ))[$index]; - } - - /** @return string[] */ - public function getHcInMulticastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_IN_MULTICAST_PACKETS); - } - - public function getHcInMulticastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_IN_MULTICAST_PACKETS, - $index - ))[$index]; - } - - /** @return string[] */ - public function getHcOutMulticastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_OUT_MULTICAST_PACKETS); - } - - public function getHcOutMulticastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_OUT_MULTICAST_PACKETS, - $index - ))[$index]; - } - - /** @return string[] */ - public function getHcInUnicastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_IN_UNICAST_PACKETS); - } - - public function getHcInUnicastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_IN_UNICAST_PACKETS, - $index - ))[$index]; - } - - /** @return string[] */ - public function getHcOutUnicastPackets() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_OUT_UNICAST_PACKETS); - } - - public function getHcOutUnicastPacketsForIndex(int $index) : string - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new( - self::OID_HC_OUT_UNICAST_PACKETS, - $index - ))[$index]; - } - - /** @return int[] */ - public function getInDiscards() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_IN_DISCARDS); - } - - public function getInDiscardsForIndex(int $index) : int - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_IN_DISCARDS, $index))[$index]; - } - - /** @return int[] */ - public function getOutDiscards() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_OUT_DISCARDS); - } - - public function getOutDiscardsForIndex(int $index) : int - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_OUT_DISCARDS, $index))[$index]; - } - - /** @return int[] */ - public function getInErrors() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_IN_ERRORS); - } - - public function getInErrorsForIndex(int $index) : int - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_IN_ERRORS, $index))[$index]; - } - - /** @return int[] */ - public function getOutErrors() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_OUT_ERRORS); - } - - public function getOutErrorsForIndex(int $index) : int - { - return $this->getSnmp()->walkFirstDegree((string) OidWithIndex::new(self::OID_OUT_ERRORS, $index))[$index]; - } - - /** @return string[] */ - public function getNames() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_NAME); - } - - /** @return int[] */ - public function getOperStatuses() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_OPER_STATUS); - } - - /** @return int[] */ - public function getSpeeds() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_SPEED); - } - - /** @return int[] */ - public function getHcSpeeds() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_HC_SPEED); - } - - /** @return int[] */ - public function getTypes() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_TYPE); - } - - /** @return mixed[] */ - public function getStackTable() : array - { - return $this->getSnmp()->walk(self::OID_STACK_STATUS); - } } diff --git a/src/Mib/Ip.php b/src/Mib/Ip.php index 044e972..a29ab41 100644 --- a/src/Mib/Ip.php +++ b/src/Mib/Ip.php @@ -4,20 +4,8 @@ namespace SimPod\PhpSnmp\Mib; -class Ip extends MibBase +final class Ip { public const OID_IP_ADDRESS = '.1.3.6.1.2.1.4.20.1.1'; public const OID_IP_NET_TO_MEDIA_PHYS_ADDRESS = '.1.3.6.1.2.1.4.22.1.2'; - - /** @return string[] */ - public function getIpAddress() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_IP_ADDRESS); - } - - /** @return string[] */ - public function getIpNetToMediaPhysAddress() : array - { - return $this->getSnmp()->walk(self::OID_IP_NET_TO_MEDIA_PHYS_ADDRESS); - } } diff --git a/src/Mib/Lldp.php b/src/Mib/Lldp.php index 475f997..ab8eb55 100644 --- a/src/Mib/Lldp.php +++ b/src/Mib/Lldp.php @@ -4,34 +4,10 @@ namespace SimPod\PhpSnmp\Mib; -final class Lldp extends MibBase +final class Lldp { public const OID_LOCAL_PORT_ID = '.1.0.8802.1.1.2.1.3.7.1.3'; public const OID_REMOTE_PORT_ID = '.1.0.8802.1.1.2.1.4.1.1.7'; public const OID_REMOTE_SYS_NAME = '.1.0.8802.1.1.2.1.4.1.1.9'; public const OID_PORT_CONFIG_ADMIN_STATUS = '.1.0.8802.1.1.2.1.1.6.1.2'; - - /** @return string[] */ - public function getLocalPortIds() : array - { - return $this->getSnmp()->walk(self::OID_LOCAL_PORT_ID); - } - - /** @return string[] */ - public function getRemotePortIds() : array - { - return $this->getSnmp()->walk(self::OID_REMOTE_PORT_ID); - } - - /** @return string[] */ - public function getRemoteSysNames() : array - { - return $this->getSnmp()->walk(self::OID_REMOTE_SYS_NAME); - } - - /** @return int[] */ - public function getPortConfigAdminStatus() : array - { - return $this->getSnmp()->walk(self::OID_PORT_CONFIG_ADMIN_STATUS); - } } diff --git a/src/Mib/Mib.php b/src/Mib/Mib.php deleted file mode 100644 index c6ff7b6..0000000 --- a/src/Mib/Mib.php +++ /dev/null @@ -1,14 +0,0 @@ - null, - HostResources::OID_HR_DEVICE_TYPES => null, - HostResources::OID_HR_FSTYPES => null, - ]; - - /** @var Snmp */ - private $snmp; - - public function __construct(Snmp $snmp) - { - $this->snmp = $snmp; - } - - public function getSnmp() : Snmp - { - return $this->snmp; - } -} diff --git a/src/Mib/Object/EntityClass.php b/src/Mib/Object/EntityClass.php index 03e5c53..a8ddcdc 100644 --- a/src/Mib/Object/EntityClass.php +++ b/src/Mib/Object/EntityClass.php @@ -4,9 +4,7 @@ namespace SimPod\PhpSnmp\Mib\Object; -use Consistence\Enum\Enum; - -class EntityClass extends Enum +final class EntityClass { public const OTHER = 1; public const UNKNOWN = 2; @@ -19,59 +17,4 @@ class EntityClass extends Enum public const MODULE = 9; public const PORT = 10; public const STACK = 11; - - public static function getOther() : self - { - return self::get(self::OTHER); - } - - public static function getUnknown() : self - { - return self::get(self::UNKNOWN); - } - - public static function getChassis() : self - { - return self::get(self::CHASSIS); - } - - public static function getBackplane() : self - { - return self::get(self::BACKPLANE); - } - - public static function getContainer() : self - { - return self::get(self::CONTAINER); - } - - public static function getPowerSupply() : self - { - return self::get(self::POWER_SUPPLY); - } - - public static function getFan() : self - { - return self::get(self::FAN); - } - - public static function getSensor() : self - { - return self::get(self::SENSOR); - } - - public static function getModule() : self - { - return self::get(self::MODULE); - } - - public static function getPort() : self - { - return self::get(self::PORT); - } - - public static function getStack() : self - { - return self::get(self::STACK); - } } diff --git a/src/Mib/Object/HrDeviceStatus.php b/src/Mib/Object/HrDeviceStatus.php index 8fdc542..a693f0c 100644 --- a/src/Mib/Object/HrDeviceStatus.php +++ b/src/Mib/Object/HrDeviceStatus.php @@ -4,7 +4,7 @@ namespace SimPod\PhpSnmp\Mib\Object; -class HrDeviceStatus +final class HrDeviceStatus { public const UNKNOWN = 1; public const RUNNING = 2; diff --git a/src/Mib/Object/HrDeviceType.php b/src/Mib/Object/HrDeviceType.php index 37422fc..993da60 100644 --- a/src/Mib/Object/HrDeviceType.php +++ b/src/Mib/Object/HrDeviceType.php @@ -4,7 +4,7 @@ namespace SimPod\PhpSnmp\Mib\Object; -class HrDeviceType +final class HrDeviceType { public const HR_DEVICE_OTHER = 1; public const HR_DEVICE_UNKNOWN = 2; diff --git a/src/Mib/SNMPFramework.php b/src/Mib/SNMPFramework.php deleted file mode 100644 index 6c64f84..0000000 --- a/src/Mib/SNMPFramework.php +++ /dev/null @@ -1,16 +0,0 @@ -getSnmp()->walkFirstDegree(self::OID_SNMP_ENGINE_TIME); - } -} diff --git a/src/Mib/SnmpFramework.php b/src/Mib/SnmpFramework.php new file mode 100644 index 0000000..e33a1a4 --- /dev/null +++ b/src/Mib/SnmpFramework.php @@ -0,0 +1,10 @@ +getSnmp()->walkFirstDegree(self::OID_DESCRIPTION); - } - - /** @return string[] */ - public function getLocation() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_LOCATION); - } - - /** @return string[] */ - public function getName() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_NAME); - } - - /** @return int[] */ - public function getUptime() : array - { - return $this->getSnmp()->walkFirstDegree(self::OID_UPTIME); - } } diff --git a/src/OidWithIndex.php b/src/OidWithIndex.php deleted file mode 100644 index d6447c7..0000000 --- a/src/OidWithIndex.php +++ /dev/null @@ -1,32 +0,0 @@ -oid = $oid; - $this->index = $index; - } - - public static function new(string $oid, int $index) : self - { - return new self($oid, $index); - } - - public function __toString() : string - { - return sprintf('%s.%d', $this->oid, $this->index); - } -} diff --git a/src/Snmp.php b/src/Snmp.php deleted file mode 100644 index 18e5948..0000000 --- a/src/Snmp.php +++ /dev/null @@ -1,302 +0,0 @@ -community = $community; - $this->host = $host; - $this->retry = $retry; - $this->timeout = $timeout; - $this->version = $version; - - $this->secName = $community; - $this->secLevel = $secLevel; - $this->authProtocol = $authProtocol; - $this->authPassphrase = $authPassphrase; - $this->privProtocol = $privProtocol; - $this->privPassphrase = $privPassphrase; - snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC); - } - - /** @return mixed[] */ - public function walkFirstDegree(string $walkedOid) : array - { - $result = $this->realWalk($walkedOid); - - $processedResult = []; - - $oidPrefix = null; - foreach ($result as $oid => $value) { - $length = strrpos($oid, '.'); - assert(is_int($length)); - - if ($oidPrefix !== null && $oidPrefix !== substr($oid, 0, $length)) { - throw new SnmpFailed('Requested OID tree is not a first degree indexed SNMP value'); - } - - $oidPrefix = substr($oid, 0, $length); - - $processedResult[substr($oid, $length + 1)] = $this->parseSnmpValue($value); - } - - return $processedResult; - } - - /** @return mixed[] */ - public function realWalk(string $oid) : array - { - switch ($this->getVersion()) { - case '1': - $result = @snmprealwalk( - $this->host, - $this->community, - $oid, - $this->getTimeout(), - $this->getRetry() - ); - break; - case '2c': - $result = @snmp2_real_walk( - $this->host, - $this->community, - $oid, - $this->getTimeout(), - $this->getRetry() - ); - break; - case '3': - $result = @snmp3_real_walk( - $this->host, - $this->getSecName(), - $this->getSecLevel(), - $this->getAuthProtocol(), - $this->getAuthPassphrase(), - $this->getPrivProtocol(), - $this->getPrivPassphrase(), - $oid, - $this->getTimeout(), - $this->getRetry() - ); - break; - default: - throw new SnmpFailed('Invalid SNMP version: ' . $this->getVersion()); - } - - if ($result === false) { - throw new SnmpFailed('Could not perform walk for OID ' . $oid); - } - - return $result; - } - - public function getSecName() : string - { - return $this->secName; - } - - public function getSecLevel() : string - { - return $this->secLevel; - } - - public function getAuthProtocol() : string - { - return $this->authProtocol; - } - - public function getAuthPassphrase() : string - { - return $this->authPassphrase; - } - - public function getPrivProtocol() : string - { - return $this->privProtocol; - } - - public function getPrivPassphrase() : string - { - return $this->privPassphrase; - } - - /** @return mixed[] */ - public function walk(string $oid) : array - { - $rawResult = $this->realWalk($oid); - - $result = []; - foreach ($rawResult as $oidKey => $value) { - $result[$oidKey] = $this->parseSnmpValue($value); - } - - return $result; - } - - private function getVersion() : string - { - return $this->version; - } - - private function getTimeout() : int - { - return $this->timeout; - } - - private function getRetry() : int - { - return $this->retry; - } - - /** @return int|string */ - private function parseSnmpValue(string $rawValue) - { - if ($rawValue === '""' || $rawValue === '') { - return ''; - } - - [$type, $value] = explode(':', $rawValue, 2); - - $value = trim($value); - - switch ($type) { - case 'STRING': - $resolvedValue = strpos($value, '"') === 0 ? trim(substr(substr($value, 1), 0, -1)) : $value; - break; - - case 'INTEGER': - if (is_numeric($value)) { - $resolvedValue = (int) $value; - } else { - // find the first digit and offset the string to that point - // just in case there is some mib strangeness going on - preg_match('/\d/', $value, $m, PREG_OFFSET_CAPTURE); - $resolvedValue = (int) substr($value, $m[0][1]); - } - - break; - - case 'Float': - $resolvedValue = (float) $value; - break; - - case 'BITS': - // This is vaguely implemented - $resolvedValue = $value; - break; - - case 'Counter32': - $resolvedValue = (int) $value; - break; - - case 'Counter64': - $resolvedValue = $value; - break; - - case 'Gauge32': - $resolvedValue = (int) $value; - break; - - case 'Hex-STRING': - $resolvedValue = $value; - break; - - case 'IpAddress': - $resolvedValue = $value; - break; - - case 'Opaque': - $resolvedValue = $this->parseSnmpValue(str_replace('Oprague: ', '', $value)); - break; - - case 'OID': - $objectTypes = MibBase::OBJECT_TYPES; - $reversedOidParts = explode('.', strrev($value), 2); - $objectTypeOidBase = strrev($reversedOidParts[1]); - $resolvedValue = array_key_exists( - $objectTypeOidBase, - $objectTypes - ) ? (int) $reversedOidParts[0] : $value; - break; - - case 'Timeticks': - $length = strrpos($value, ')'); - assert(is_int($length)); - $resolvedValue = (int) substr($value, 1, $length - 1); - break; - - default: - throw new SnmpFailed('ERR: Unhandled SNMP return type: ' . $type); - } - - return $resolvedValue; - } -} diff --git a/src/Transport/ApiSnmpClient.php b/src/Transport/ApiSnmpClient.php new file mode 100644 index 0000000..4403c0b --- /dev/null +++ b/src/Transport/ApiSnmpClient.php @@ -0,0 +1,157 @@ +client = $client; + $this->requestFactory = $requestFactory; + $this->streamFactory = $streamFactory; + $this->apiHostUrl = $apiHostUrl; + $this->host = $host; + $this->community = $community; + $this->timeout = $timeout; + $this->retries = $retries; + $this->version = $version; + } + + /** @inheritDoc */ + public function get(array $oids) : array + { + return $this->executeRequest(self::REQUEST_TYPE_GET, $oids); + } + + /** @inheritDoc */ + public function getNext(array $oids) : array + { + return $this->executeRequest(self::REQUEST_TYPE_GET_NEXT, $oids); + } + + /** @inheritDoc */ + public function walk(string $oid) : array + { + return $this->executeRequest(self::REQUEST_TYPE_WALK, [$oid]); + } + + /** + * @param string[] $oids + * + * @return array + */ + private function executeRequest(string $requestType, array $oids) : array + { + $body = json_encode( + [ + 'request_type' => $requestType, + 'host' => $this->host, + 'community' => $this->community, + 'oids' => $oids, + 'version' => $this->version, + 'timeout' => $this->timeout, + 'retries' => $this->retries, + ] + ); + + $request = $this->requestFactory->createRequest('POST', $this->apiHostUrl . self::API_PATH) + ->withBody($this->streamFactory->createStream($body)); + + try { + $response = $this->client->sendRequest($request); + } catch (Throwable $throwable) { + throw GeneralException::new($throwable->getMessage(), $throwable); + } + + $result = json_decode((string) $response->getBody(), true, 4, JSON_BIGINT_AS_STRING); + if (array_key_exists('error', $result)) { + if (preg_match('~no such object: (.+)~', $result['error'], $matches) === 1) { + throw NoSuchObjectExists::withOid($matches[1]); + } + + if (preg_match('~no such instance: (.+)~', $result['error'], $matches) === 1) { + throw NoSuchInstanceExists::withOid($matches[1]); + } + + if (preg_match('~end of mib: (.+)~', $result['error'], $matches) === 1) { + throw EndOfMibReached::withOid($matches[1]); + } + + throw GeneralException::new($result['error']); + } + + $oidsAndValues = $result['result']; + + $result = []; + for ($i = 0, $iMax = count($oidsAndValues); $i < $iMax; $i += 2) { + $key = $oidsAndValues[$i]; + assert(is_string($key)); + $value = $oidsAndValues[$i + 1]; + + $result[$key] = $value; + } + + return $result; + } +} diff --git a/src/Transport/Cli/ProcessExecutor.php b/src/Transport/Cli/ProcessExecutor.php new file mode 100644 index 0000000..b06690d --- /dev/null +++ b/src/Transport/Cli/ProcessExecutor.php @@ -0,0 +1,11 @@ +commandTimeout = $commandTimeout; + } + + /** @inheritDoc */ + public function execute(array $args) : string + { + try { + $process = new Process($args); + $process->setTimeout($this->commandTimeout); + $process->run(); + + if (! $process->isSuccessful()) { + throw GeneralException::new($process->getErrorOutput()); + } + + return $process->getOutput(); + } catch (Throwable $throwable) { + if ($throwable instanceof SnmpException) { + throw $throwable; + } + + throw GeneralException::new($throwable->getMessage(), $throwable); + } + } +} diff --git a/src/Transport/CliSnmpClient.php b/src/Transport/CliSnmpClient.php new file mode 100644 index 0000000..a6b37e7 --- /dev/null +++ b/src/Transport/CliSnmpClient.php @@ -0,0 +1,143 @@ +processExecutor = $processExecutor ?? new SymfonyProcessProcessExecutor(120); + $this->processArgs = [ + '-ObenU', + '-t', + (string) $timeout, + '-r', + (string) $retries, + '-v', + $version, + '-c', + $community, + $host, + ]; + $this->useBulk = $version === '2c'; + } + + /** @inheritDoc */ + public function get(array $oids) : array + { + try { + return $this->processOutput( + $this->processExecutor->execute(array_merge(['snmpget'], $this->processArgs, $oids)) + ); + } catch (GeneralException $exception) { + // check for SNMP v1 + if (preg_match('~\(noSuchName\).+Failed object: (.+?)$~ms', $exception->getMessage(), $matches) === 1) { + throw NoSuchInstanceExists::withOid($matches[1]); + } + + throw $exception; + } + } + + /** @inheritDoc */ + public function getNext(array $oids) : array + { + try { + return $this->processOutput( + $this->processExecutor->execute(array_merge(['snmpgetnext'], $this->processArgs, $oids)) + ); + } catch (GeneralException $exception) { + // check for SNMP v1 + if (preg_match('~\(noSuchName\).+Failed object: (.+?)$~ms', $exception->getMessage(), $matches) === 1) { + throw EndOfMibReached::withOid($matches[1]); + } + + throw $exception; + } + } + + /** @inheritDoc */ + public function walk(string $oid) : array + { + $walker = $this->useBulk ? 'snmpbulkwalk' : 'snmpwalk'; + $output = $this->processExecutor->execute(array_merge([$walker], $this->processArgs, [$oid])); + + $result = $this->processOutput($output); + if (count($result) === 0) { + throw NoSuchInstanceExists::withOid($oid); + } + + return $result; + } + + /** @return array */ + private function processOutput(string $output) : array + { + $result = []; + + foreach (preg_split("~\r\n|\r|\n~", $output) as $line) { + if ($line === '') { + continue; + } + + // check for SNMP v1 + if ($line === 'End of MIB') { + throw EndOfMibReached::new(); + } + + [$oid, $value] = explode(' = ', $line, 2); + + if (strpos($value, 'No Such Object') === 0) { + throw NoSuchObjectExists::withOid($oid); + } + + if (strpos($value, 'No Such Instance') === 0) { + throw NoSuchInstanceExists::withOid($oid); + } + + if (strpos($value, 'No more variables left in this MIB View') === 0) { + throw EndOfMibReached::withOid($oid); + } + + $result[$oid] = ValueParser::parse($value); + } + + return $result; + } +} diff --git a/src/Transport/ExtensionSnmpClient.php b/src/Transport/ExtensionSnmpClient.php new file mode 100644 index 0000000..e33a5c2 --- /dev/null +++ b/src/Transport/ExtensionSnmpClient.php @@ -0,0 +1,125 @@ +snmp = new SNMP($snmpVersion, $host, $community, $timeout, $retry); + $this->snmp->oid_output_format = SNMP_OID_OUTPUT_NUMERIC; + $this->snmp->exceptions_enabled = SNMP::ERRNO_ANY; + if ($snmpVersion !== SNMP::VERSION_3) { + return; + } + + $this->snmp->setSecurity($secLevel, $authProtocol, $authPassphrase, $privProtocol, $privPassphrase); + } + + /** @inheritDoc */ + public function get(array $oids) : array + { + try { + return $this->processOutput($this->snmp->get($oids)); + } catch (Throwable $throwable) { + throw $this->processException($throwable); + } + } + + /** @inheritDoc */ + public function getNext(array $oids) : array + { + try { + return $this->processOutput($this->snmp->getnext($oids)); + } catch (Throwable $throwable) { + throw $this->processException($throwable); + } + } + + /** @inheritDoc */ + public function walk(string $oid) : array + { + try { + return $this->processOutput($this->snmp->walk($oid, false, 50)); + } catch (Throwable $throwable) { + throw $this->processException($throwable); + } + } + + /** + * @param array $output + * + * @return array + */ + private function processOutput(array $output) : array + { + $result = []; + foreach ($output as $oid => $value) { + $result[$oid] = ValueParser::parse($value); + } + + return $result; + } + + private function processException(Throwable $throwable) : Throwable + { + if ($throwable instanceof SnmpException) { + return $throwable; + } + + if (strpos($throwable->getMessage(), 'No Such Object') !== false) { + return NoSuchObjectExists::fromThrowable($throwable); + } + + if (strpos($throwable->getMessage(), 'No Such Instance') !== false + || strpos($throwable->getMessage(), 'noSuchName') !== false) { + return NoSuchInstanceExists::fromThrowable($throwable); + } + + if (strpos($throwable->getMessage(), 'No more variables left in this MIB View') !== false) { + return EndOfMibReached::fromThrowable($throwable); + } + + return GeneralException::new($throwable->getMessage(), $throwable); + } +} diff --git a/src/Transport/FallbackSnmpClient.php b/src/Transport/FallbackSnmpClient.php new file mode 100644 index 0000000..1350b94 --- /dev/null +++ b/src/Transport/FallbackSnmpClient.php @@ -0,0 +1,68 @@ +snmpClients = $snmpClients; + } + + /** @inheritDoc */ + public function get(array $oids) : array + { + return $this->tryClients( + static function (SnmpClient $client) use ($oids) : array { + return $client->get($oids); + } + ); + } + + /** @inheritDoc */ + public function getNext(array $oids) : array + { + return $this->tryClients( + static function (SnmpClient $client) use ($oids) : array { + return $client->getNext($oids); + } + ); + } + + /** @inheritDoc */ + public function walk(string $oid) : array + { + return $this->tryClients( + static function (SnmpClient $client) use ($oid) : array { + return $client->walk($oid); + } + ); + } + + /** @return mixed */ + private function tryClients(callable $requestCallback) + { + foreach ($this->snmpClients as $snmpClient) { + try { + return $requestCallback($snmpClient); + } catch (GeneralException $exception) { + // try next client + } + } + + /** @phpstan-ignore-next-line exception will always be there */ + throw GeneralException::new('all SNMP clients failed, last error: ' . $exception->getMessage(), $exception); + } +} diff --git a/src/Transport/SnmpClient.php b/src/Transport/SnmpClient.php new file mode 100644 index 0000000..ce03354 --- /dev/null +++ b/src/Transport/SnmpClient.php @@ -0,0 +1,25 @@ + + */ + public function get(array $oids) : array; + + /** + * @param string[] $oids + * + * @return array + */ + public function getNext(array $oids) : array; + + /** @return array */ + public function walk(string $oid) : array; +} diff --git a/src/Transport/ValueParser.php b/src/Transport/ValueParser.php new file mode 100644 index 0000000..77729c8 --- /dev/null +++ b/src/Transport/ValueParser.php @@ -0,0 +1,40 @@ +expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.4' + ); + + throw EndOfMibReached::fromThrowable($exception); + } + + public function testFromThrowableWithUnexpectedMessage() : void + { + $exception = new Exception('unexpected message'); + + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage('No more variables left in this MIB View (It is past the end of the MIB tree)'); + + throw EndOfMibReached::fromThrowable($exception); + } +} diff --git a/tests/Exception/NoSuchInstanceTest.php b/tests/Exception/NoSuchInstanceTest.php new file mode 100644 index 0000000..b75a2fd --- /dev/null +++ b/tests/Exception/NoSuchInstanceTest.php @@ -0,0 +1,32 @@ +expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.4'); + + throw NoSuchInstanceExists::fromThrowable($exception); + } + + public function testFromThrowableWithUnexpectedMessage() : void + { + $exception = new Exception('unexpected message'); + + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID'); + + throw NoSuchInstanceExists::fromThrowable($exception); + } +} diff --git a/tests/Exception/NoSuchObjectTest.php b/tests/Exception/NoSuchObjectTest.php new file mode 100644 index 0000000..926c34f --- /dev/null +++ b/tests/Exception/NoSuchObjectTest.php @@ -0,0 +1,32 @@ +expectException(NoSuchObjectExists::class); + $this->expectExceptionMessage('No Such Object available on this agent at this OID: .1.4'); + + throw NoSuchObjectExists::fromThrowable($exception); + } + + public function testFromThrowableWithUnexpectedMessage() : void + { + $exception = new Exception('unexpected message'); + + $this->expectException(NoSuchObjectExists::class); + $this->expectExceptionMessage('No Such Object available on this agent at this OID'); + + throw NoSuchObjectExists::fromThrowable($exception); + } +} diff --git a/tests/Helpers/OidsStripperTest.php b/tests/Helpers/OidsStripperTest.php new file mode 100644 index 0000000..2a7aa07 --- /dev/null +++ b/tests/Helpers/OidsStripperTest.php @@ -0,0 +1,49 @@ + 'a', + '.1.2.3.2' => 'b', + '.1.2.3.3' => 'c', + ]; + + $expected = [ + 1 => 'a', + 2 => 'b', + 3 => 'c', + ]; + + self::assertSame($expected, OidStripper::stripLeafOidsParentOid($raw)); + } + + public function testWalk() : void + { + $raw = [ + '.1.2.3.1' => 'a', + '.1.2.3.2' => 'b', + '.1.2.3.3' => 'c', + ]; + + $expected = [ + '3.1' => 'a', + '3.2' => 'b', + '3.3' => 'c', + ]; + + $snmpClient = $this->createMock(SnmpClient::class); + $snmpClient->expects(self::once())->method('walk')->with($oid = '.1.2')->willReturn($raw); + + self::assertSame($expected, OidStripper::walk($snmpClient, $oid)); + } +} diff --git a/tests/Helpers/SingleValueTest.php b/tests/Helpers/SingleValueTest.php new file mode 100644 index 0000000..ff0f868 --- /dev/null +++ b/tests/Helpers/SingleValueTest.php @@ -0,0 +1,20 @@ + 'a']; + + $expected = 'a'; + + self::assertSame($expected, SingleValue::get($raw)); + } +} diff --git a/tests/Transport/ApiSnmpClientTest.php b/tests/Transport/ApiSnmpClientTest.php new file mode 100644 index 0000000..c4581cf --- /dev/null +++ b/tests/Transport/ApiSnmpClientTest.php @@ -0,0 +1,325 @@ +createApiSnmp(); + + $response = <<client->method('sendRequest') + ->with( + self::callback( + static function (RequestInterface $request) : bool { + $json = <<getUri() === 'http://localhost/snmp-proxy' + && self::jsonIsIdentical($json, (string) $request->getBody()); + } + ) + ) + ->willReturn($this->createResponse($response)); + + $result = $apiSnmp->get(['.1.3.6.1.2.1.25.2.3.1.2.1', '.1.3.6.1.2.1.25.2.3.1.2.4']); + + self::assertSame( + [ + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + ], + $result + ); + } + + public function testGetNext() : void + { + $apiSnmp = $this->createApiSnmp(); + + $response = <<client->method('sendRequest') + ->with( + self::callback( + static function (RequestInterface $request) : bool { + $json = <<getUri() === 'http://localhost/snmp-proxy' + && self::jsonIsIdentical($json, (string) $request->getBody()); + } + ) + ) + ->willReturn($this->createResponse($response)); + + $result = $apiSnmp->getNext(['.1.3.6.1.2.1.25.2.3.1.2', '.1.3.6.1.2.1.25.2.3.1.2.3']); + + self::assertSame( + [ + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + ], + $result + ); + } + + public function testWalk() : void + { + $apiSnmp = $this->createApiSnmp(); + + $response = <<client->method('sendRequest') + ->with( + self::callback( + static function (RequestInterface $request) : bool { + $json = <<getUri() === 'http://localhost/snmp-proxy' + && self::jsonIsIdentical($json, (string) $request->getBody()); + } + ) + ) + ->willReturn($this->createResponse($response)); + + $result = $apiSnmp->walk('.1.3.6.1.2.1.31.1.1.1.15'); + + self::assertSame( + [ + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + ], + $result + ); + } + + public function testThatParametersAreCorrectlyPropagatedToTheJsonRequest() : void + { + $this->client = $this->createMock(Client::class); + $psr71Factory = new Psr17Factory(); + + $apiSnmp = new ApiSnmpClient( + $this->client, + $psr71Factory, + $psr71Factory, + 'http://somewhere', + 'lorem', + 'ipsum', + 50, + 5, + '1' + ); + + $response = <<client->method('sendRequest') + ->with( + self::callback( + static function (RequestInterface $request) : bool { + $json = <<getUri() === 'http://somewhere/snmp-proxy' + && self::jsonIsIdentical($json, (string) $request->getBody()); + } + ) + ) + ->willReturn($this->createResponse($response)); + + $result = $apiSnmp->get(['.1.3.6.1.2.1.2.2.1.2.1000009']); + + self::assertSame(['.1.3.6.1.2.1.2.2.1.2.1000009' => 'Port-Channel9'], $result); + } + + public function testWalkWithEndOfMibError() : void + { + $apiSnmp = $this->createApiSnmp(); + + $this->client->method('sendRequest') + ->willReturn($this->createResponse('{"error": "end of mib: .1.15"}')); + + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $apiSnmp->walk('.1.15'); + } + + public function testWalkWithNoSuchInstanceError() : void + { + $apiSnmp = $this->createApiSnmp(); + + $this->client->method('sendRequest') + ->willReturn($this->createResponse('{"error": "no such instance: .1.3.6.1.2.1.1.1"}')); + + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.6.1.2.1.1.1'); + + $apiSnmp->walk('.1.3.6.1.2.1.1.1'); + } + + public function testWalkWithNoSuchObjectError() : void + { + $apiSnmp = $this->createApiSnmp(); + + $this->client->method('sendRequest') + ->willReturn($this->createResponse('{"error": "no such object: .1.4"}')); + + $this->expectException(NoSuchObjectExists::class); + $this->expectExceptionMessage('No Such Object available on this agent at this OID: .1.4'); + + $apiSnmp->walk('.1.4'); + } + + public function testWalkWithRequestError() : void + { + $apiSnmp = $this->createApiSnmp(); + + $this->client->method('sendRequest') + ->willThrowException(new Exception('some error')); + + $this->expectException(GeneralException::class); + $this->expectExceptionMessage('Unexpected error: some error'); + + $apiSnmp->walk('.1.4'); + } + + public function testWalkWithUnexpectedError() : void + { + $apiSnmp = $this->createApiSnmp(); + + $this->client->method('sendRequest') + ->willReturn($this->createResponse('{"error": "something unexpected happened"}')); + + $this->expectException(GeneralException::class); + $this->expectExceptionMessage('Unexpected error: something unexpected happened'); + + $apiSnmp->walk('.1.4'); + } + + private function createApiSnmp() : ApiSnmpClient + { + $this->client = $this->createMock(Client::class); + $psr71Factory = new Psr17Factory(); + + return new ApiSnmpClient($this->client, $psr71Factory, $psr71Factory, 'http://localhost'); + } + + private function createResponse(string $body) : ResponseInterface + { + $stream = $this->createMock(StreamInterface::class); + $stream->method('__toString')->willReturn($body); + + $response = $this->createMock(ResponseInterface::class); + $response->method('getBody')->willReturn($stream); + + return $response; + } +} diff --git a/tests/Transport/Cli/SymfonyProcessProcessExecutorTest.php b/tests/Transport/Cli/SymfonyProcessProcessExecutorTest.php new file mode 100644 index 0000000..2ddc106 --- /dev/null +++ b/tests/Transport/Cli/SymfonyProcessProcessExecutorTest.php @@ -0,0 +1,42 @@ +expectException(GeneralException::class); + $this->expectExceptionMessage(sprintf('Unexpected error: sh: 1: exec: %s: not found', $command)); + + $executor = new SymfonyProcessProcessExecutor(1); + $executor->execute([$command]); + } + + public function testTimeout() : void + { + $this->expectException(GeneralException::class); + $this->expectExceptionMessage( + sprintf('Unexpected error: The process "%s" exceeded the timeout of 1 seconds', "'sleep' '5'") + ); + + $time = microtime(true); + + $executor = new SymfonyProcessProcessExecutor(1); + $executor->execute(['sleep', '5']); + + self::assertLessThan(2, microtime(true) - $time); + } +} diff --git a/tests/Transport/CliSnmpClientTest.php b/tests/Transport/CliSnmpClientTest.php new file mode 100644 index 0000000..2232a1d --- /dev/null +++ b/tests/Transport/CliSnmpClientTest.php @@ -0,0 +1,244 @@ + ['file', '/dev/null', 'w'], 2 => ['file', '/dev/null', 'w']], $pipes); + if ($process === false) { + self::fail('failed to initiate SNMP agent'); + } + + self::$process = $process; + } + + public static function tearDownAfterClass() : void + { + if (self::$process === null) { + return; + } + + shell_exec(sprintf('pkill -2 -P %d', proc_get_status(self::$process)['pid'])); + self::$process = null; + } + + public function __destruct() + { + self::tearDownAfterClass(); + } + + public function testGet() : void + { + $result = $this->createCliSnmp()->get(['.1.3.6.1.2.1.25.2.3.1.2.1', '.1.3.6.1.2.1.25.2.3.1.2.4']); + + self::assertSame( + [ + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + ], + $result + ); + } + + public function testGetNext() : void + { + $result = $this->createCliSnmp()->getNext(['.1.3.6.1.2.1.25.2.3.1.2', '.1.3.6.1.2.1.25.2.3.1.2.3']); + + self::assertSame( + [ + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + ], + $result + ); + } + + public function testWalk() : void + { + $result = $this->createCliSnmp()->walk('.1.3.6.1.2.1.31.1.1.1.15'); + + self::assertSame( + [ + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + ], + $result + ); + } + + public function testWalkWithOldSnmpVersion() : void + { + $result = $this->createCliSnmp('1')->walk('.1.3.6.1.2.1.31.1.1.1.15'); + + self::assertSame( + [ + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + ], + $result + ); + } + + public function testWalkWholeTree() : void + { + $result = $this->createCliSnmp()->walk('.1.3'); + + self::assertSame( + [ + '.1.3.6.1.2.1.1.3.0' => 293718542, + '.1.3.6.1.2.1.2.2.1.2.47' => 'Ethernet47', + '.1.3.6.1.2.1.2.2.1.2.48' => 'Ethernet48', + '.1.3.6.1.2.1.2.2.1.2.49001' => 'Ethernet49/1', + '.1.3.6.1.2.1.2.2.1.2.50001' => 'Ethernet50/1', + '.1.3.6.1.2.1.2.2.1.2.1000008' => 'Port-Channel8', + '.1.3.6.1.2.1.2.2.1.2.1000009' => 'Port-Channel9', + '.1.3.6.1.2.1.2.2.1.2.2002002' => 'Vlan2002', + '.1.3.6.1.2.1.2.2.1.2.2002019' => 'Vlan2019', + '.1.3.6.1.2.1.2.2.1.2.2002020' => 'Vlan2020', + '.1.3.6.1.2.1.2.2.1.2.5000000' => 'Loopback0', + '.1.3.6.1.2.1.2.2.1.14.8' => 0, + '.1.3.6.1.2.1.2.2.1.14.9' => 226, + '.1.3.6.1.2.1.2.2.1.14.10' => 256, + '.1.3.6.1.2.1.2.2.1.14.11' => 296, + '.1.3.6.1.2.1.4.20.1.1.10.100.192.2' => '10.100.192.2', + '.1.3.6.1.2.1.4.20.1.1.10.110.27.254' => '10.110.27.254', + '.1.3.6.1.2.1.4.20.1.1.66.208.216.74' => '66.208.216.74', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.97' => '91 E2 BA E3 5A 61', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.99' => '53 54 00 5F 41 D0', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.100' => '53 54 00 4C 5A 5D', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.102' => '53 54 00 A9 A8 3B', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.104' => '53 54 00 5A A0 CA', + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.2' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.3' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + '.1.3.6.1.2.1.31.1.1.1.6.46' => '1884401752869190', + '.1.3.6.1.2.1.31.1.1.1.6.47' => '1883620653799494', + '.1.3.6.1.2.1.31.1.1.1.6.48' => '1884283891426650', + '.1.3.6.1.2.1.31.1.1.1.6.49001' => '2494191363092125', + '.1.3.6.1.2.1.31.1.1.1.6.50001' => '17658827020872235', + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + '.1.3.6.1.6.3.10.2.1.3.0' => 2937024, + ], + $result + ); + } + + public function testInvalidVersion() : void + { + $this->expectException(InvalidVersionProvided::class); + $this->expectExceptionMessage('Invalid or unsupported SNMP version "whatever"'); + + $this->createCliSnmp('whatever')->walk('.1.15'); + } + + public function testWalkWithNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createCliSnmp()->walk('.1.3.5'); + } + + public function testWalkWithSnmpVersion1AndNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createCliSnmp('1')->walk('.1.3.5'); + } + + public function testWalkWithEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $this->createCliSnmp()->walk('.1.15'); + } + + public function testWalkWithSnmpVersion1AndEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage('No more variables left in this MIB View (It is past the end of the MIB tree)'); + + $this->createCliSnmp('1')->walk('.1.15'); + } + + public function testGetWithNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createCliSnmp()->get(['.1.3.5']); + } + + public function testGetWithSnmpVersion1AndNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createCliSnmp('1')->get(['.1.3.5']); + } + + public function testGetNextWithEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $this->createCliSnmp()->getNext(['.1.15']); + } + + public function testGetNextWithSnmpVersion1AndEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $this->createCliSnmp('1')->getNext(['.1.15']); + } + + public function testWalkWithUnknownTypeError() : void + { + $this->expectException(CannotParseUnknownValueType::class); + $this->expectExceptionMessage('Encountered unknown value type "OPAQUE"'); + + $this->createCliSnmp()->walk('.1.6.6.6.666'); + } + + private function createCliSnmp(string $version = '2c') : CliSnmpClient + { + return new CliSnmpClient(self::SNMP_HOST, 'public', 5, 3, $version); + } +} diff --git a/tests/Transport/ExtensionSnmpClientTest.php b/tests/Transport/ExtensionSnmpClientTest.php new file mode 100644 index 0000000..10d9749 --- /dev/null +++ b/tests/Transport/ExtensionSnmpClientTest.php @@ -0,0 +1,225 @@ + ['file', '/dev/null', 'w'], 2 => ['file', '/dev/null', 'w']], $pipes); + if ($process === false) { + self::fail('failed to initiate SNMP agent'); + } + + self::$process = $process; + } + + public static function tearDownAfterClass() : void + { + if (self::$process === null) { + return; + } + + shell_exec(sprintf('pkill -2 -P %d', proc_get_status(self::$process)['pid'])); + self::$process = null; + } + + public function __destruct() + { + self::tearDownAfterClass(); + } + + public function testGet() : void + { + $result = $this->createExtensionSnmp()->get(['.1.3.6.1.2.1.2.2.1.14.9', '.1.3.6.1.2.1.4.20.1.1.10.100.192.2']); + + self::assertSame( + [ + '.1.3.6.1.2.1.2.2.1.14.9' => 226, + '.1.3.6.1.2.1.4.20.1.1.10.100.192.2' => '10.100.192.2', + ], + $result + ); + } + + public function testGetNext() : void + { + $result = $this->createExtensionSnmp()->getNext( + ['.1.3.6.1.2.1.2.2.1.14.9', '.1.3.6.1.2.1.4.20.1.1.10.100.192.2'] + ); + + self::assertSame( + [ + '.1.3.6.1.2.1.2.2.1.14.10' => 256, + '.1.3.6.1.2.1.4.20.1.1.10.110.27.254' => '10.110.27.254', + ], + $result + ); + } + + public function testWalk() : void + { + $result = $this->createExtensionSnmp()->walk('.1.3.6.1.2.1.31.1.1.1.15'); + + self::assertSame( + [ + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + ], + $result + ); + } + + public function testWalkWholeTree() : void + { + $result = $this->createExtensionSnmp()->walk('.1.3'); + + self::assertSame( + [ + '.1.3.6.1.2.1.1.3.0' => 293718542, + '.1.3.6.1.2.1.2.2.1.2.47' => 'Ethernet47', + '.1.3.6.1.2.1.2.2.1.2.48' => 'Ethernet48', + '.1.3.6.1.2.1.2.2.1.2.49001' => 'Ethernet49/1', + '.1.3.6.1.2.1.2.2.1.2.50001' => 'Ethernet50/1', + '.1.3.6.1.2.1.2.2.1.2.1000008' => 'Port-Channel8', + '.1.3.6.1.2.1.2.2.1.2.1000009' => 'Port-Channel9', + '.1.3.6.1.2.1.2.2.1.2.2002002' => 'Vlan2002', + '.1.3.6.1.2.1.2.2.1.2.2002019' => 'Vlan2019', + '.1.3.6.1.2.1.2.2.1.2.2002020' => 'Vlan2020', + '.1.3.6.1.2.1.2.2.1.2.5000000' => 'Loopback0', + '.1.3.6.1.2.1.2.2.1.14.8' => 0, + '.1.3.6.1.2.1.2.2.1.14.9' => 226, + '.1.3.6.1.2.1.2.2.1.14.10' => 256, + '.1.3.6.1.2.1.2.2.1.14.11' => 296, + '.1.3.6.1.2.1.4.20.1.1.10.100.192.2' => '10.100.192.2', + '.1.3.6.1.2.1.4.20.1.1.10.110.27.254' => '10.110.27.254', + '.1.3.6.1.2.1.4.20.1.1.66.208.216.74' => '66.208.216.74', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.97' => '91 E2 BA E3 5A 61', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.99' => '53 54 00 5F 41 D0', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.100' => '53 54 00 4C 5A 5D', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.102' => '53 54 00 A9 A8 3B', + '.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.104' => '53 54 00 5A A0 CA', + '.1.3.6.1.2.1.25.2.3.1.2.1' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.2' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.3' => '.1.3.6.1.2.1.25.2.1.2', + '.1.3.6.1.2.1.25.2.3.1.2.4' => '.1.3.6.1.2.1.25.2.1.9', + '.1.3.6.1.2.1.31.1.1.1.6.46' => '1884401752869190', + '.1.3.6.1.2.1.31.1.1.1.6.47' => '1883620653799494', + '.1.3.6.1.2.1.31.1.1.1.6.48' => '1884283891426650', + '.1.3.6.1.2.1.31.1.1.1.6.49001' => '2494191363092125', + '.1.3.6.1.2.1.31.1.1.1.6.50001' => '17658827020872235', + '.1.3.6.1.2.1.31.1.1.1.15.1000001' => 100000, + '.1.3.6.1.2.1.31.1.1.1.15.1000003' => 60000, + '.1.3.6.1.2.1.31.1.1.1.15.1000005' => 80000, + '.1.3.6.1.6.3.10.2.1.3.0' => 2937024, + ], + $result + ); + } + + public function testWalkWithInvalidVersion() : void + { + $this->expectException(InvalidVersionProvided::class); + $this->expectExceptionMessage('Invalid or unsupported SNMP version "whatever"'); + + (new ExtensionSnmpClient('', '', 0, 0, 'whatever'))->walk('.1.15'); + } + + public function testWalkWithEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $this->createExtensionSnmp()->walk('.1.15'); + } + + public function testWalkWithSnmpVersion1AndEndOfMibError() : void + { + // SNMP v1 reports NoSuchInstance instead of EndOfMib + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.15'); + + $this->createExtensionSnmp('1')->walk('.1.15'); + } + + public function testGetWithNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createExtensionSnmp()->get(['.1.3.5']); + } + + public function testGetWithSnmpVersion1AndNoSuchInstanceError() : void + { + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.3.5'); + + $this->createExtensionSnmp('1')->get(['.1.3.5']); + } + + public function testGetNextWithEndOfMibError() : void + { + $this->expectException(EndOfMibReached::class); + $this->expectExceptionMessage( + 'No more variables left in this MIB View (It is past the end of the MIB tree), tried oid: .1.15' + ); + + $this->createExtensionSnmp()->getNext(['.1.15']); + } + + public function testGetNextWithSnmpVersion1AndEndOfMibError() : void + { + // SNMP v1 reports NoSuchInstance instead of EndOfMib + $this->expectException(NoSuchInstanceExists::class); + $this->expectExceptionMessage('No Such Instance currently exists at this OID: .1.15'); + + $this->createExtensionSnmp('1')->getNext(['.1.15']); + } + + public function testWalkWithUnknownType() : void + { + $this->expectException(CannotParseUnknownValueType::class); + $this->expectExceptionMessage('Encountered unknown value type "OPAQUE"'); + + $this->createExtensionSnmp()->walk('.1.6.6.6.666'); + } + + public function testWalkHostThatDoesntExist() : void + { + $this->expectException(GeneralException::class); + $this->expectExceptionMessage('Unexpected error: No response from 127.0.0.1:1'); + + (new ExtensionSnmpClient('127.0.0.1:1', 'public', 100, 0))->walk('.1.6.6.6.666'); + } + + private function createExtensionSnmp(string $version = '2c') : ExtensionSnmpClient + { + return new ExtensionSnmpClient(self::SNMP_HOST, 'public', 5000000, 3, $version); + } +} diff --git a/tests/Transport/FallbackSnmpClientTest.php b/tests/Transport/FallbackSnmpClientTest.php new file mode 100644 index 0000000..82b6863 --- /dev/null +++ b/tests/Transport/FallbackSnmpClientTest.php @@ -0,0 +1,109 @@ +createMock(SnmpClient::class); + $client1->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willReturn($expected = ['.1.2.3' => 123]); + + $fallbackClient = new FallbackSnmpClient($client1); + $result = $fallbackClient->get($oids); + + self::assertSame($expected, $result); + } + + public function testGetNext() : void + { + $client1 = $this->createMock(SnmpClient::class); + $client1->expects(self::once()) + ->method('getNext') + ->with($oids = ['.1.2.3']) + ->willReturn($expected = ['.1.2.3' => 123]); + + $fallbackClient = new FallbackSnmpClient($client1); + $result = $fallbackClient->getNext($oids); + + self::assertSame($expected, $result); + } + + public function testWalk() : void + { + $client1 = $this->createMock(SnmpClient::class); + $client1->expects(self::once()) + ->method('walk') + ->with($oid = '.1.2.3') + ->willReturn($expected = ['.1.2.3' => 123]); + + $fallbackClient = new FallbackSnmpClient($client1); + $result = $fallbackClient->walk($oid); + + self::assertSame($expected, $result); + } + + public function testOnlyLastClientWorks() : void + { + $client1 = $this->createMock(SnmpClient::class); + $client1->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willThrowException(GeneralException::new('an error')); + + $client2 = $this->createMock(SnmpClient::class); + $client2->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willThrowException(GeneralException::new('other error')); + + $client3 = $this->createMock(SnmpClient::class); + $client3->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willReturn($expected = ['.1.2.3' => 123]); + + $fallbackClient = new FallbackSnmpClient($client1, $client2, $client3); + $result = $fallbackClient->get($oids); + + self::assertSame($expected, $result); + } + + public function testAllClientsFail() : void + { + $client1 = $this->createMock(SnmpClient::class); + $client1->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willThrowException(GeneralException::new('an error')); + + $client2 = $this->createMock(SnmpClient::class); + $client2->expects(self::once()) + ->method('get') + ->with($oids = ['.1.2.3']) + ->willThrowException($expected = GeneralException::new('other error')); + + $fallbackClient = new FallbackSnmpClient($client1, $client2); + + $this->expectExceptionObject($expected); + + $fallbackClient->get($oids); + } + + public function testNoClientsProvided() : void + { + $this->expectExceptionObject(GeneralException::new('no SNMP clients provided')); + + new FallbackSnmpClient(); + } +} diff --git a/tests/Transport/ValueParserTest.php b/tests/Transport/ValueParserTest.php new file mode 100644 index 0000000..98f7172 --- /dev/null +++ b/tests/Transport/ValueParserTest.php @@ -0,0 +1,44 @@ + */ + public function providerParse() : iterable + { + yield 'Counter64' => ['Counter64: 123456', '123456']; + yield 'Hex-STRING' => ['Hex-STRING: A1 B2 C3', 'A1 B2 C3']; + yield 'IpAddress' => ['IpAddress: 127.0.0.1', '127.0.0.1']; + yield 'OID' => ['OID: .1.2.3', '.1.2.3']; + yield 'STRING' => ['STRING: "abc"', 'abc']; + yield 'INTEGER' => ['INTEGER: 123', 123]; + yield 'Counter32' => ['Counter32: 123', 123]; + yield 'Gauge32' => ['Gauge32: 123', 123]; + yield 'Timeticks' => ['Timeticks: (3171608622) 367 days, 2:01:26.22', 3171608622]; + } + + public function testParseWithUnknownValueType() : void + { + $this->expectException(CannotParseUnknownValueType::class); + $this->expectExceptionMessage('Encountered unknown value type "Wow"'); + + ValueParser::parse('Wow: lol'); + } +} diff --git a/tests/Transport/data/public.snmpwalk b/tests/Transport/data/public.snmpwalk new file mode 100644 index 0000000..1c693f5 --- /dev/null +++ b/tests/Transport/data/public.snmpwalk @@ -0,0 +1,56 @@ +# system uptime +.1.3.6.1.2.1.1.3.0 = 293718542 + +# interface descriptions +.1.3.6.1.2.1.2.2.1.2.47 = STRING: "Ethernet47" +.1.3.6.1.2.1.2.2.1.2.48 = STRING: "Ethernet48" +.1.3.6.1.2.1.2.2.1.2.49001 = STRING: "Ethernet49/1" +.1.3.6.1.2.1.2.2.1.2.50001 = STRING: "Ethernet50/1" +.1.3.6.1.2.1.2.2.1.2.1000008 = STRING: "Port-Channel8" +.1.3.6.1.2.1.2.2.1.2.1000009 = STRING: "Port-Channel9" +.1.3.6.1.2.1.2.2.1.2.2002002 = STRING: "Vlan2002" +.1.3.6.1.2.1.2.2.1.2.2002019 = STRING: "Vlan2019" +.1.3.6.1.2.1.2.2.1.2.2002020 = STRING: "Vlan2020" +.1.3.6.1.2.1.2.2.1.2.5000000 = STRING: "Loopback0" + +# interface in errors +.1.3.6.1.2.1.2.2.1.14.8 = Counter32: 0 +.1.3.6.1.2.1.2.2.1.14.9 = Counter32: 226 +.1.3.6.1.2.1.2.2.1.14.10 = Counter32: 256 +.1.3.6.1.2.1.2.2.1.14.11 = Counter32: 296 + +# IP addresses +.1.3.6.1.2.1.4.20.1.1.10.100.192.2 = IpAddress: 10.100.192.2 +.1.3.6.1.2.1.4.20.1.1.10.110.27.254 = IpAddress: 10.110.27.254 +.1.3.6.1.2.1.4.20.1.1.66.208.216.74 = IpAddress: 66.208.216.74 + +# IP addresses to MAC addresses +.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.97 = Hex-STRING: 91 E2 BA E3 5A 61 +.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.99 = Hex-STRING: 53 54 00 5F 41 D0 +.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.100 = Hex-STRING: 53 54 00 4C 5A 5D +.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.102 = Hex-STRING: 53 54 00 A9 A8 3B +.1.3.6.1.2.1.4.22.1.2.2000955.185.152.67.104 = Hex-STRING: 53 54 00 5A A0 CA + +# host resource storage type +.1.3.6.1.2.1.25.2.3.1.2.1 = OID: .1.3.6.1.2.1.25.2.1.2 +.1.3.6.1.2.1.25.2.3.1.2.2 = OID: .1.3.6.1.2.1.25.2.1.2 +.1.3.6.1.2.1.25.2.3.1.2.3 = OID: .1.3.6.1.2.1.25.2.1.2 +.1.3.6.1.2.1.25.2.3.1.2.4 = OID: .1.3.6.1.2.1.25.2.1.9 + +# interface in octets +.1.3.6.1.2.1.31.1.1.1.6.46 = Counter64: 1884401752869190 +.1.3.6.1.2.1.31.1.1.1.6.47 = Counter64: 1883620653799494 +.1.3.6.1.2.1.31.1.1.1.6.48 = Counter64: 1884283891426650 +.1.3.6.1.2.1.31.1.1.1.6.49001 = Counter64: 2494191363092125 +.1.3.6.1.2.1.31.1.1.1.6.50001 = Counter64: 17658827020872235 + +# interface HC speed +.1.3.6.1.2.1.31.1.1.1.15.1000001 = Gauge32: 100000 +.1.3.6.1.2.1.31.1.1.1.15.1000003 = Gauge32: 60000 +.1.3.6.1.2.1.31.1.1.1.15.1000005 = Gauge32: 80000 + +# SNMP engine time +.1.3.6.1.6.3.10.2.1.3.0 = INTEGER: 2937024 + +# made up thing just for testing - didn't find real examples :D +.1.6.6.6.666.0 = OPAQUE: AA BB CC diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 45cf08f..f3c23c6 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimPod\GraphQL\Utils; +namespace SimPod\PhpSnmp\Tests; use function date_default_timezone_set; use function error_reporting;