Skip to content

Commit

Permalink
module name changed to windowsdnsserver-py
Browse files Browse the repository at this point in the history
  - package name renamed to windowsdnsserver-py
  - more pythonic naming style
  - fix is_dns_server_module_installed
  • Loading branch information
bilalekremharmansa committed Oct 30, 2020
1 parent 4579f4f commit bb70cde
Show file tree
Hide file tree
Showing 21 changed files with 179 additions and 149 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## windowsdnsserver-py changelog

### version 0.0.1

- DnsService implementation with PowerShell DNSServerModule
- DnsService supports only TXT and A records for now
- wrapper class to execute PowerShell commands on server -- PowerShellRunner
- this version of module can be used as python library
- implemented with pythonic naming style (underscores, lowercase words and etc.)``
- unit tests are written for current implementation
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ endif
twine = ${python} -m twine
pip = ${python} -m pip

upload: build
install-local: build-module
${pip} uninstall windowsdnsserver-py
${pip} install windowsdnsserver-py --no-index --find-links dist/

upload: build-module
@${twine} upload dist/* -r testpypi

build:
upload-prod: build-module
@${twine} upload dist/* -r pypi

build-module:
rm -rf build
rm -rf dist
${python} setup.py sdist
${python} setup.py bdist_wheel
-
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## microsoftdnsserver-py
## windowsdnsserver-py

microsoftdnsserver-py is a wrapper Python library for [DnsServer](https://docs.microsoft.com/en-us/powershell/module/dnsserver/?view=win10-ps) module.
windowsdnsserver-py is a wrapper Python library for [DnsServer](https://docs.microsoft.com/en-us/powershell/module/dnsserver/?view=win10-ps) module.

Subprocess module is used to perform process calls to interact with DnsServer module.

Expand All @@ -12,9 +12,11 @@ Subprocess module is used to perform process calls to interact with DnsServer mo
## Installation

```shell
pip install windowsdnserver-py
```

## Limitations
- Libray is not able to work remotely, currently, it is able to call DnsServer module on localhost.
- Python 3 supported, but Python 2 is not tested
- This library is not able to work remotely, currently, it is merely able to call DnsServer module on localhost.
Since, the remote session feature is not on the table, the software that uses this module
must be installed on windows server where Microsoft Dns Server is located.
must be installed on windows server where Windows Server/Dns Server is located.
25 changes: 0 additions & 25 deletions microsoftdnsserver/dns/base.py

This file was deleted.

8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
long_description = fd.read()

setuptools.setup(
name="microsoftdnsserver-py",
version="0.0.1",
name="windowsdnsserver-py",
version="0.0.1-dev1",
author="Bilal Ekrem Harmansa",
author_email="bilalekremharmansa@gmail.com",
description="wrapper Python library for DnsServer module",
description="wrapper Python library for Windows Server DnsServer module",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/bilalekremharmansa/microsoftdnsserver-py",
url="https://github.com/bilalekremharmansa/windowsdnsserver-py",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
Expand Down
16 changes: 8 additions & 8 deletions tests/test_dns_server.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import unittest


from microsoftdnsserver.dns.dnsserver import DnsServerModule
from microsoftdnsserver.dns.record import RecordType
from windowsdnsserver.dns.dnsserver import DnsServerModule
from windowsdnsserver.dns.record import RecordType


class TestDnsServer(unittest.TestCase):
Expand All @@ -14,29 +14,29 @@ def setUp(self):
self.test_dns_name = "www"

def test_get_dns_records(self):
success = self.dns.addARecord(self.test_dns_zone, self.test_dns_name, "100.100.100.100")
success = self.dns.add_a_record(self.test_dns_zone, self.test_dns_name, "100.100.100.100")
self.assertTrue(success, "failed while adding test record")

records = self.dns.getDNSRecords(self.test_dns_zone, self.test_dns_name, RecordType.A)
records = self.dns.get_dns_records(self.test_dns_zone, self.test_dns_name, RecordType.A)
self.assertGreater(len(records), 0)

for record in records:
self.assertEqual(record.type, RecordType.A)

self.dns.removeARecord(self.test_dns_zone, self.test_dns_name)
self.dns.remove_a_record(self.test_dns_zone, self.test_dns_name)

def test_add_record_a(self):
ip_addr = "10.0.0.99"

success = self.dns.addARecord(self.test_dns_zone, self.test_dns_name, ip_addr)
success = self.dns.add_a_record(self.test_dns_zone, self.test_dns_name, ip_addr)
self.assertTrue(success)

def test_add_record_txt(self):
txt = "my test record"

success = self.dns.addTxtRecord(self.test_dns_zone, self.test_dns_name, txt)
success = self.dns.add_txt_record(self.test_dns_zone, self.test_dns_name, txt)
self.assertTrue(success)


if __name__ == '__main__':
unittest.main()
unittest.main()
54 changes: 27 additions & 27 deletions tests/test_dns_server_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@

from unittest.mock import patch

from microsoftdnsserver.command_runner.runner import Result
from microsoftdnsserver.dns.dnsserver import DnsServerModule
from microsoftdnsserver.dns.record import RecordType
from microsoftdnsserver.util.dns_server_utils import parseTtl, formatTtl
from windowsdnsserver.command_runner.runner import Result
from windowsdnsserver.dns.dnsserver import DnsServerModule
from windowsdnsserver.dns.record import RecordType
from windowsdnsserver.util.dns_server_utils import parse_ttl, format_ttl


class TestDnsServerUtils(unittest.TestCase):

def test_convert_dns_server(self):
mock_data = self.load_mock_data()

with patch('microsoftdnsserver.dns.dnsserver.DnsServerModule.run') as mock:
with patch('windowsdnsserver.dns.dnsserver.DnsServerModule.run') as mock:
mock.return_value = Result(True, 0, mock_data['GetDnsServerResponse1'], '')

dns = DnsServerModule()
results = dns.getDNSRecords("zone")
results = dns.get_dns_records("zone")
print(results)
self.assertEqual(len(results), 1)

Expand All @@ -39,48 +39,48 @@ def mock_time_to_live(h, m, s):
mock['Seconds'] = s
return mock

self.assertEqual(parseTtl(mock_time_to_live(1, 23, 50)), '1h 23m 50s')
self.assertEqual(parse_ttl(mock_time_to_live(1, 23, 50)), '1h 23m 50s')

self.assertEqual(parseTtl(mock_time_to_live(0, 23, 50)), '23m 50s')
self.assertEqual(parseTtl(mock_time_to_live(1, 0, 50)), '1h 50s')
self.assertEqual(parseTtl(mock_time_to_live(1, 23, 0)), '1h 23m')
self.assertEqual(parseTtl(mock_time_to_live(1, 50, 2)), '1h 50m 2s')
self.assertEqual(parse_ttl(mock_time_to_live(0, 23, 50)), '23m 50s')
self.assertEqual(parse_ttl(mock_time_to_live(1, 0, 50)), '1h 50s')
self.assertEqual(parse_ttl(mock_time_to_live(1, 23, 0)), '1h 23m')
self.assertEqual(parse_ttl(mock_time_to_live(1, 50, 2)), '1h 50m 2s')

def test_format_ttl(self):
self.assertEqual(formatTtl('10h 20m 30s'), '10:20:30')
self.assertEqual(formatTtl('10h 30s'), '10:00:30')
self.assertEqual(formatTtl('30s'), '00:00:30')
self.assertEqual(formatTtl('20m 10h 30s'), '10:20:30')
self.assertEqual(formatTtl('0s 10h 20m'), '10:20:00')
self.assertEqual(format_ttl('10h 20m 30s'), '10:20:30')
self.assertEqual(format_ttl('10h 30s'), '10:00:30')
self.assertEqual(format_ttl('30s'), '00:00:30')
self.assertEqual(format_ttl('20m 10h 30s'), '10:20:30')
self.assertEqual(format_ttl('0s 10h 20m'), '10:20:00')

with self.assertRaisesRegex(Exception, 'time unit could not be determined'):
formatTtl('10n')
format_ttl('10n')

with self.assertRaisesRegex(AssertionError, "empty ttl value"):
formatTtl('')
format_ttl('')

with self.assertRaises(AssertionError):
formatTtl(111)
format_ttl(111)

with self.assertRaises(Exception):
formatTtl('10h 20m 30')
format_ttl('10h 20m 30')

with self.assertRaises(Exception):
formatTtl('10h 20')
format_ttl('10h 20')

with self.assertRaises(Exception):
formatTtl('0h 0m 0s')
format_ttl('0h 0m 0s')

self.assertEqual(formatTtl('0h 0m 1s'), '00:00:01')
self.assertEqual(format_ttl('0h 0m 1s'), '00:00:01')

with self.assertRaisesRegex(AssertionError, 'hour can not be more than'):
formatTtl('25h')
format_ttl('25h')

with self.assertRaisesRegex(AssertionError, 'minute can not be more than'):
formatTtl('90m')
format_ttl('90m')

with self.assertRaisesRegex(AssertionError, 'seconds can not be more than'):
formatTtl('100s')
format_ttl('100s')

def load_mock_data(self):
fd = open('/tests/mock_data.json')
Expand All @@ -90,4 +90,4 @@ def load_mock_data(self):


if __name__ == '__main__':
unittest.main()
unittest.main()
14 changes: 7 additions & 7 deletions tests/test_records.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import unittest

from microsoftdnsserver.dns.record import RecordType
from microsoftdnsserver.util import dns_server_utils
from windowsdnsserver.dns.record import RecordType
from windowsdnsserver.util import dns_server_utils


class TestRecords(unittest.TestCase):

def test_supported_record_types(self):
self.assertTrue(dns_server_utils.isRecordTypeSupported('A'))
self.assertTrue(dns_server_utils.isRecordTypeSupported('Txt'))
self.assertTrue(dns_server_utils.is_record_type_supported('A'))
self.assertTrue(dns_server_utils.is_record_type_supported('Txt'))

self.assertFalse(dns_server_utils.isRecordTypeSupported('SOA'))
self.assertFalse(dns_server_utils.isRecordTypeSupported('TXT'))
self.assertFalse(dns_server_utils.is_record_type_supported('SOA'))
self.assertFalse(dns_server_utils.is_record_type_supported('TXT'))

def test_record_type_value_of(self):
record_type_a = RecordType.value_of('A')
Expand All @@ -24,4 +24,4 @@ def test_record_type_value_of(self):


if __name__ == '__main__':
unittest.main()
unittest.main()
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import subprocess
import sys

from .runner import Command, CommandRunner, Result
from ..util import logger


DEFAULT_POWERSHELL_EXE_PATH = "C:\Windows\syswow64\WindowsPowerShell\\v1.0\powershell.exe"
DEFAULT_POWER_SHELL_EXE_PATH = "C:\Windows\syswow64\WindowsPowerShell\\v1.0\powershell.exe"


class PowerShellCommand(Command):
Expand All @@ -16,7 +17,7 @@ def __init__(self, cmdlet: str, *flags, **args):
self.flags = flags
self.args = args

def prepareCommand(self):
def build(self):
cmd = [self.cmdlet]

# add flags, ie -Force
Expand All @@ -33,24 +34,21 @@ def prepareCommand(self):

return cmd

def _postProcessResult(self):
pass


class PowerShellRunner(CommandRunner):

def __init__(self, powerShellPath: str = None):
self.logger = logger.createLogger("PowerShellRunner")
def __init__(self, power_shell_path: str = None):
self.logger = logger.create_logger("PowerShellRunner")

self.powerShellPath = powerShellPath
if powerShellPath is None:
self.powerShellPath = DEFAULT_POWERSHELL_EXE_PATH
self.power_shell_path = power_shell_path
if power_shell_path is None:
self.power_shell_path = DEFAULT_POWER_SHELL_EXE_PATH

def run(self, command: PowerShellCommand) -> Result:
assert isinstance(command, PowerShellCommand)

cmd = command.prepareCommand()
cmd.insert(0, self.powerShellPath)
cmd = command.build()
cmd.insert(0, self.power_shell_path)

self.logger.debug("Running: [%s]" % ' '.join(cmd))

Expand All @@ -63,8 +61,10 @@ def run(self, command: PowerShellCommand) -> Result:
finally:
pass

out = out.decode('utf-8')
err = err.decode('utf-8')
self.logger.debug('using default encoding: [%s]' % sys.stdout.encoding)

out = out.decode(sys.stdout.encoding, 'replace')
err = err.decode(sys.stdout.encoding, 'replace')

self.logger.debug("Returned: \n\tout:[%s], \n\terr:[%s]" % (out, err))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from microsoftdnsserver.exception.exception_common import MethodNotImplementedError
from windowsdnsserver.exception.exception_common import MethodNotImplementedError


class Command(object):

def prepareCommand(self):
def build(self):
raise MethodNotImplementedError()


Expand Down
File renamed without changes.
25 changes: 25 additions & 0 deletions windowsdnsserver/dns/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import List

from windowsdnsserver.dns.record import Record, RecordType
from windowsdnsserver.exception.exception_common import MethodNotImplementedError


class DNSService(object):

def __init__(self):
pass

def get_dns_records(self, zone: str, name: str, record_type: RecordType) -> List[Record]:
raise MethodNotImplementedError()

def add_a_record(self, zone: str, name: str, ip: str, ttl: str) -> bool:
raise MethodNotImplementedError()

def remove_a_record(self, zone: str, name: str) -> bool:
raise MethodNotImplementedError()

def add_txt_record(self, zone: str, name: str, content, ttl: str) -> bool:
raise MethodNotImplementedError()

def remove_txt_record(self, zone: str, name: str) -> bool:
raise MethodNotImplementedError()
Loading

0 comments on commit bb70cde

Please sign in to comment.