many changes -- read full commit

* Improved docstrings, README
* Added argument --version and its implementation
* Added "uninstall" rule to Makefile and added it in the README
* Updated CHANGELOG
* Cleaned up code

I've probably forgot something so we can't merge to master yet.
And we still have to add installation instructions for ubuntu and fedora
This commit is contained in:
Pranav Jerry 2021-08-14 22:00:07 +05:30
parent fe146b7a0d
commit d7a9edc1e8
Signed by: pranav
GPG Key ID: F1DCDC4FED0A0C5B
8 changed files with 160 additions and 49 deletions

View File

@ -1,16 +1,18 @@
# Changelog
## [Unreleased][] - 2021-08-13
## [Unreleased][] - 2021-08-14
### Added
- Support for arguments
- Configuration file support
- New versioning scheme that conforms to PEP 440
- Made messages more readable
- Improved documentation in docstrings
### Changed
- Changed default name of adhoc network. This will make naxalnet
- Changed default name of mesh network. This will make naxalnet
incompatible with previous versions.
## [v0.2.0][] - 2021-07-26

View File

@ -1,5 +1,8 @@
# This makefile uses setup.py under the hood
# This makefile uses setup.py under the hood.
# In ubuntu, python and pip are symlinks to python2 and pip2.
# So we have to specify python as python3 by default.
PYTHON := python3
PIP := pip3
DESTDIR:= /
all: build
@ -10,5 +13,10 @@ build:
install: build
$(PYTHON) setup.py install --root="$(DESTDIR)" --optimize=1 --skip-build
uninstall:
$(PIP) uninstall naxalnet
rm -rf /usr/share/naxalnet /usr/lib/systemd/system/naxalnet.service
clean:
rm -rf build naxalnet.egg-info

View File

@ -200,14 +200,15 @@ When a shutdown occurs, [enable naxalnet][enablenx]
## Uninstalling
If you installed naxalnet manually, there is no way to uninstall
than manually removing the files:
If you installed naxalnet manually, use make uninstall to remove
naxalnet and its data files:
```sh
sudo pip uninstall naxalnet
sudo rm -rf /usr/share/naxalnet* /usr/lib/systemd/system/naxalnet.service
sudo make uninstall
```
This requires python pip to be installed.
## Contributing or reporting bugs
See [HACKING.md](HACKING.md)

View File

@ -36,4 +36,4 @@ See README.md for documentation.
#
# In case you forgot to add a version, put the next number
# in the next commit
__version__ = "0.2.0a3.dev2"
__version__ = "0.2.0a3.dev3"

View File

@ -8,16 +8,48 @@ This file contains functions to parse configuration files
and arguments. Most of these functions are meant to be used
by parse_args() internally, so only parse_args() should
be imported outside this file.
Some parts of naxalnet can be configured by configuration
files and arguments. First, the default values from
default.py is taken. Then, key-value pairs from the
configuration files are read, if they exist, in the
following order:
- First it reads /usr/share/naxalnet/naxalnet.conf
and then from /usr/share/naxalnet/naxalnet.conf.d/*.conf
where *.conf means any file with the name ending with
.conf
- Then, it looks for the files naxalnet.conf and
naxalnet.conf.d/*.conf from the directory
/etc/naxalnet, like it did up above.
- Then it parses the arguments from the commandline,
storing the values in the files parsed until now
as fallback. Finally you get an argpase.Namespace object
from parse_args().
Because of the way this is implemented, all key-value
pairs in the configuration should have an argument
too, or they won't be parsed.
All the key-value pairs are replaced if they exist
by each new configuration file parsed, similar
to how systemd parses configuration and service files.
If any of the files checked does not exist, then they are
ignored and the next combination is checked. If none of
the config files exist and no arguments are given, the
fallback data from default.py is used.
"""
from pathlib import Path
from configparser import ConfigParser
from argparse import ArgumentParser
from argparse import ArgumentParser, Namespace
from naxalnet.default import CONFIG, CONFIG_FILES, CONFIG_DIRS
def get_config_files():
"""returns list of configuration files as Path objects to parse"""
"""
Read list of configuration files and return a list
of files that exists as pathlib.Path objects
"""
config_files = []
for directory in CONFIG_DIRS:
path = Path(directory)
@ -43,7 +75,7 @@ def parse_config():
return parser
def parse_args():
def parse_args() -> Namespace:
"""
Parse all arguments and return ArgumentParser.parse_args(),
with values in config files as fallback. Ideally, only this
@ -69,6 +101,7 @@ def parse_args():
help="password of the WiFi AP",
default=config["ap"]["passwd"],
)
parser.add_argument(
"--adhoc-name",
"-a",
@ -76,32 +109,33 @@ def parse_args():
default=config["adhoc"]["name"],
help="name of adhoc network",
)
# TODO: print info about wifi network from config and args and exit
parser.add_argument(
"--print-wifi",
action="store_true",
default=False,
help="prints the ssid and password of the WiFi network and exit",
)
parser.add_argument(
"--networkd-config-dir",
type=str,
default=config["networkd"]["confdir"],
help="the directory where systemd-networkd configuration files are stored",
)
parser.add_argument(
"--networkd-runtime-dir",
type=str,
default=config["networkd"]["runtimedir"],
help="the directory where configuration files of systemd-networkd should be copied",
help="volatile directory where configuration files of systemd-networkd should be copied",
)
parser.add_argument(
"--version",
default=False,
action="store_true",
help="prints the version and exit",
)
# TODO: implement --verbose
# parser.add_argument(
# "-v",
# "--verbose",
# help="increase output verbosity; can be used multiple times",
# action="count",
# default=0,
# )
return parser.parse_args()

View File

@ -1,8 +1,15 @@
#!/usr/bin/env python3
"""
default.py
----------
This file contains default values for configuration.
The values are likely to be replaced by other configuration files.
This is taken as fallback data by config.py if no
configuration files were found, or if a key-value pair
was not present in the config file. The data will be
further changed by arguments if naxalnet is called
from the commandline. See config.py for more info.
"""
CONFIG = {
@ -15,5 +22,5 @@ CONFIG = {
}
# glob
CONFIG_FILES = ["naxalnet.conf", "naxalnet.d/*.conf"]
CONFIG_DIRS = ["/etc"]
CONFIG_FILES = ["naxalnet.conf", "naxalnet.conf.d/*.conf"]
CONFIG_DIRS = ["/usr/share/naxalnet", "/etc/naxalnet"]

View File

@ -16,7 +16,48 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Manage wifi adapter via iwd D-Bus api"""
"""
iwd.py
------
This file contains methods to communicate with iwd via
its D-Bus API and control WiFi adapters.
Some terms used here, such as device and adapter might
confuse you if you haven't used iwctl before.
Just as a quick reference, here is a list of terms
and what they mean:
- ad-hoc: a mode supported by some WiFi adapters to
start a decentralised network, where there
is no central point of failure.
- ap: a mode used to start a central access point
so that other machines without naxalnet can
connect to the mesh. AP is also known as
WiFi hotspot.
- station: this is the mode most WiFi adapters use
by default. This mode is used to connect to
an ap. naxalnet DOES NOT use this mode.
- adapter: a physical WiFi chip or something similar
that is present inside most laptops and phones
or can be connected via USB to a machine.
- device: an interface provided by the kernel to control
an adapter. Some adapters can have multiple
devices so that you can start an ap on one device
and an ad-hoc on the other. By default, iwd starts
only one device each for one adapter.
- machine: Since iwd uses the term device for a
WiFi interface, we use the word machine to
refer to a computer, or a laptop, or a phone.
- node: a machine that runs naxalnet and is therefore
connected to the mesh.
"""
from dasbus.connection import SystemMessageBus
@ -27,7 +68,7 @@ IWD_ADAPTER_INTERFACE = "net.connman.iwd.Adapter"
# If you are new to D-Bus, you might want to use a program
# such as D-Feet (https://wiki.gnome.org/Apps/DFeet) for reference.
# And try out iwctl to understand iwd's bus objects
# And try out iwctl to understand iwd's bus objects.
class IWD:
@ -155,23 +196,24 @@ class Device:
# name of adapter ('phy0' for example)
self.adapter = self._iwd.get_name_from_path(adapter_path)
def is_adhoc_started(self):
def is_adhoc_started(self) -> bool:
"""
Returns True if an adhoc network is started on this device.
Returns None if device is not powered on or not in ad-hoc mode.
Returns False if the network is in staring stage, device
is not powered on or not in ad-hoc mode.
"""
if self.is_powered_on() and self.get_mode() == "ad-hoc":
return self._proxy.Started
# If above condition is not true, return None
return None
# If above condition is not true, return False
return False
def is_ap_started(self):
def is_ap_started(self) -> bool:
"""
Same as is_adhoc_started(), but for ap
"""
if self.is_powered_on() and self.get_mode() == "ap":
return self._proxy.Started
return None
return False
def get_mode(self) -> str:
"""
@ -250,8 +292,6 @@ class Adapter:
self._proxy = self._bus.get_proxy(IWD_BUS, self._path)
self.name = self._proxy.Name
self.supported_modes = self._proxy.SupportedModes
self.model = self._proxy.Model
self.vendor = self._proxy.Vendor
def is_powered_on(self) -> bool:
"""returns True if adapter is powered on, False otherwise"""

View File

@ -16,24 +16,25 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Setup a working BATMAN Advanced network
with systemd-networkd and iwd
scripts.py
----------
The functions in this file is used for reading configs, args
and doing the things this program is supposed to do.
This file is named scripts.py because the original developer
of this program could not think of a better name that suits this file.
If you want to hack naxalnet, this is the right place to start.
When run from the commandline, the function here_be_dragons() is called.
"""
import sys
from pathlib import Path
from shutil import copy
from dasbus.error import DBusError
from naxalnet.iwd import IWD, Device, Adapter
from naxalnet import __version__
from naxalnet.iwd import Adapter, Device, IWD
from naxalnet.config import parse_args
NETWORKD_CONFIGS = "/usr/share/naxalnet/networkd"
NETWORKD_VOLATILE_DIR = "/run/systemd/network"
# default values
ADHOC_NAME = "HelloWorld"
AP_SSID = "NaxalNet"
AP_PASSWD = "naxalnet256"
def copy_files(args):
"""
@ -59,7 +60,8 @@ def setup_devices(args):
"""
Setup wifi interfaces using iwd
This function should be called every time an interface
is connected or removed
is connected or removed.
args should be what parse_args() returns
"""
iwd = IWD()
devices = iwd.get_devices()
@ -68,6 +70,9 @@ def setup_devices(args):
# Find devices supporting ad-hoc and ap
for i in devices:
# For each device, check if its adapter supports
# ad-hoc or ap. Many adapters will support both,
# so we will prioritise ad-hoc over ap.
device = Device(i)
adapter = Adapter(device.adapter)
if adapter.supports_mode("ad-hoc"):
@ -80,10 +85,10 @@ def setup_devices(args):
adhoc_device = Device(adhoc_devices.pop())
# The same device is likely to have ap support too.
# But we can't start ad-hoc and ap on the same interface.
# Remove adhoc_device from ap_devices if it exists there
# So we will remove adhoc_device from ap_devices if it exists there
if adhoc_device.name in ap_devices:
ap_devices.remove(adhoc_device.name)
print("Working on ad-hoc")
print("Starting mesh on", adhoc_device.name)
# Turn on adapter if it is off
# See issue #9
adhoc_adapter = Adapter(adhoc_device.adapter)
@ -94,8 +99,9 @@ def setup_devices(args):
# Start Access point if ap_device is not empty,
# ie, we have more devices
if len(ap_devices) != 0:
print("Working on AP")
ap_device = Device(ap_devices.pop())
print("Starting WiFi Access point on", ap_device.name)
print('Use "naxalnet --print-wifi" to get password')
# Turn on adapter if it is off
# See issue #9
ap_adapter = Adapter(ap_device.adapter)
@ -106,21 +112,34 @@ def setup_devices(args):
def print_wifi(args):
"""
Prints the name and password of the adhoc, and ap
from the arguments
"""
print("Mesh name:", args.adhoc_name)
print("SSID:", args.ap_ssid)
print("Password:", args.ap_passwd)
def print_version():
"""Just does what the name suggests"""
print(__version__)
def here_be_dragons():
"""
This is where the magic happens!
This function is run every time you
execute naxalnet from commandline
execute naxalnet from the commandline
"""
args = parse_args()
if args.print_wifi:
print_wifi(args)
sys.exit(0)
elif args.version:
print_version()
sys.exit(0)
try:
copy_files(args)