Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamic gmp protocol selection #141

Merged
merged 2 commits into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added a `password only` credential type [PR 133](https://github.com/greenbone/python-gvm/pull/133)
* Added [type hints](https://docs.python.org/3/library/typing.html) for Gmpv8
[PR 136](https://github.com/greenbone/python-gvm/pull/136)
* Added dynamic selection of the Gmp class depending on the GMP version supported
by the remote manager daemon
[PR 141](https://github.com/greenbone/python-gvm/pull/141)

### Changed
* Renamed `create_asset` method to `create_host` and dropped asset_type
Expand Down
15 changes: 15 additions & 0 deletions docs/api/gmp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.. _gmp:

.. avoid TOCtree warning by marking this file als orphan

:orphan:

GMP
^^^
.. automodule:: gvm.protocols.gmp

Protocol
--------

.. autoclass:: Gmp
:members:
6 changes: 6 additions & 0 deletions docs/api/protocols.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Protocols
gmpv8
ospv1

Dynamic
^^^^^^^

To dynamically use the supported GMP version of the manager daemon see
:mod:`gvm.protocols.gmp` for details.

Latest
^^^^^^

Expand Down
105 changes: 105 additions & 0 deletions gvm/protocols/gmp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""
Module for communication with gvmd
"""
from typing import Any, Optional, Callable

from gvm.errors import GvmError

from gvm.protocols.base import GvmProtocol, GvmConnection

from gvm.protocols.gmpv7 import Gmp as Gmpv7
from gvm.protocols.gmpv8 import Gmp as Gmpv8

from gvm.transforms import EtreeCheckCommandTransform

from gvm.xml import XmlCommand


class Gmp(GvmProtocol):
"""Dynamically select supported GMP protocol of the remote manager daemon.

Must be used as a `Context Manager
<https://docs.python.org/3/reference/datamodel.html#context-managers>`_

Example:

.. code-block:: python

from gvm.protocols.gmp import Gmp

with Gmp(connection) as gmp:
# gmp can be an instance of gvm.protocols.gmpv7.Gmp or
# gvm.protocols.gmpv8.Gmp depending on the supported GMP
# version of the remote manager daemon
resp = gmp.get_tasks()

Attributes:
connection: Connection to use to talk with the remote daemon. See
:mod:`gvm.connections` for possible connection types.
transform: Optional transform `callable`_ to convert response data.
After each request the callable gets passed the plain response data
which can be used to check the data and/or conversion into different
representations like a xml dom.

See :mod:`gvm.transforms` for existing transforms.

.. _callable:
https://docs.python.org/3/library/functions.html#callable
"""

def __init__(
self,
connection: GvmConnection,
*,
transform: Optional[Callable[[str], Any]] = None
):
super().__init__(connection, transform=EtreeCheckCommandTransform())
self._gmp_transform = transform

def __enter__(self):
self.connect()
resp = self._send_xml_command(XmlCommand("get_version"))
self.disconnect()

version_el = resp.find('version')
if version_el is None:
raise GvmError(
'Invalid response from manager daemon while requesting the '
'version information.'
)

version = version_el.text
major_version = version[0]

if major_version == '7':
gmp_class = Gmpv7
elif major_version == '8':
gmp_class = Gmpv8
else:
raise GvmError(
'Remote manager daemon uses an unsupported version of GMP.'
'The GMP version was {}'.format(major_version)
)

gmp = gmp_class(self._connection, transform=self._gmp_transform)
gmp.connect()

return gmp
17 changes: 17 additions & 0 deletions tests/protocols/gmp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
87 changes: 87 additions & 0 deletions tests/protocols/gmp/test_context_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest

from tests.protocols import GmpTestCase

from gvm.errors import GvmError

from gvm.protocols.gmp import Gmp
from gvm.protocols.gmpv7 import Gmp as Gmpv7
from gvm.protocols.gmpv8 import Gmp as Gmpv8


class GmpContextManagerTestCase(GmpTestCase):

gmp_class = Gmp

def test_select_gmpv7(self):
self.connection.read.return_value(
'<get_version_response status="200" status_text="OK">'
'<version>7.0</version>'
'</get_version_response>'
)

with self.gmp as gmp:
self.assertEqual(gmp.get_protocol_version(), "7")
self.assertIsInstance(gmp, Gmpv7)

def test_select_gmpv8(self):
self.connection.read.return_value(
'<get_version_response status="200" status_text="OK">'
'<version>8.0</version>'
'</get_version_response>'
)

with self.gmp as gmp:
self.assertEqual(gmp.get_protocol_version(), "8")
self.assertIsInstance(gmp, Gmpv8)

def test_unknown_protocol(self):
self.connection.read.return_value(
'<get_version_response status="200" status_text="OK">'
'<version>1.0</version>'
'</get_version_response>'
)

with self.assertRaises(GvmError):
with self.gmp:
pass

def test_missing_version_in_response(self):
self.connection.read.return_value(
'<get_version_response status="200" status_text="OK">'
'<foo>bar</foo>'
'</get_version_response>'
)

with self.assertRaises(GvmError):
with self.gmp:
pass

def test_invalid_response(self):
self.connection.read.return_value('<get_foo_response/>')

with self.assertRaises(GvmError):
with self.gmp:
pass


if __name__ == '__main__':
unittest.main()