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

Qemu branch #8

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
92 changes: 92 additions & 0 deletions QEMU-Readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# cohydra

[![master](https://api.travis-ci.com/osmhpi/cohydra.svg?branch=master)](https://travis-ci.com/osmhpi/cohydra)

## Contributors

- Malte Andersch
- Arne Boockmeyer
- Felix Gohla
- Martin Michaelis
- Benedikt Schenkel

## Installation

### Installation With Docker

Cohydra can be obtained via docker.
The easiest solution is using the VSCode *Remote - Containers* extension.
After cloning the repository and opening it in the container, your scenarios will by executing them with `python3.7`.

Otherwise, you can build the [Dockerfile](./Dockerfile) in the project's root directory yourself by running `make`. In the container, cohydra will be added to your
`PYTHONPATH`. But you need to make sure, that you run the container with privileges to access the host network in order to have access to the host's network interfaces. You of course need to modify the volume mount to allow cohydra access to your scenarios.

```sh
docker run -it --rm --cap-add=ALL -v /var/run/docker.sock:/var/run/docker.sock --net host --pid host --userns host --privileged osmhpi/cohydra:latest
```

The main image is based on the images in the [docker](./docker) directory.
The [`cohydra-base`](./docker/cohydra-base/Dockerfile) installs all neccessary dependencies for cohydra,
[`cohydra-dev`](./docker/cohydra-dev/Dockerfile) is for development purposes (docker-cli in the container).

### Installation Without Docker

In the case you do not want to use the prebuilt docker, a normal ns-3 installation with *NetAnim* Python bindings will work, too.
The Python libraries / directory provided by ns-3 has to be in your `PYTHONPATH`, though.
Cohydra so far has only been tested with **Debian 10 Buster** and **Ubuntu 18.04 Bionic Beaver**.

There is no installation via `pip`.


## Marvis Docu:

### Installation
+ Python3.7
+ sudo apt install python3.7-dev
+ sudo apt install -y python3-pip

+ NS-3
+ Download ns-3 python wheel from: https://github.com/osmhpi/python-wheels/releases
+ wget https://github.com/osmhpi/python-wheels/releases/download/2020-04-07-15-51-05/ns-3.30-cp37-cp37m-linux_x86_64.whl

+ Docker
+ sudo apt install docker.io

+ no tty present and no askpass program specified
+ /etc/sudoers, where username=your_username
+ Add `username ALL=(ALL) NOPASSWD: ALL`

## QEMU information

### QEMU installation
+ `sudo apt-get install qemu-system-x86 ` for x86 emulations or
+ `sudo apt-get install qemu-system` for other Systems

### qcow2 file creation
+ Download a Linux image and create a qcow2 file
+ `qemu-img create -f qcow2 xxx.qcow2`
+ Start the empty qcow2 file and install the downloaded OS (qemu-system must fit the selected OS ex. arm, x86, ...)
+ `qemu-system-x86_64 -boot d -cdrom image.iso -m 512 -hda xxx.qcow2`

### Example QEMU starting arguments for testing
+ Raspberry Pi image
+ `qemu-system-arm -M versatilepb -kernel pathTo/kernel-qemu-4.14.79-stretch -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" -hda pathTo/raspbian-stretch-lite.qcow -dtb pathTo/QEMU/versatile-pb.dtb -cpu arm1176 -m 256 -no-reboot -machine versatilepb -net nic -net user,hostfwd=tcp::2222-:22,hostfwd=tcp::22280-:80`
+ Lubuntu image (kvm needs root)
+ `sudo qemu-system-x86_64 -hda pathTo/lubuntu.qcow2 -m 512 -enable-kvm -no-reboot -net nic -serial mon:stdio -net user,hostfwd=tcp::2222-:22,hostfwd=tcp::22280-:80`

### Preparations for usage (setup on QEMU image)
+ SSH
+ Possible package: `openssh-client`
+ If it won't start on system boot, a crontab can be used
+ `sudo crontab -e`
+ Add `@reboot service ssh restart`
+ Copy ssh key from Host to VM
+ ex. `ssh-copy-id -p 2222 -i pathTo/id_rsa lubuntu@127.0.0.1`
+ For the integration in Cohydra
+ Add an IP address and IP route (host side is taken care by Cohydra)
+ ex. `sudo crontab -e`
+ Add `@reboot sudo ip addr add 12.0.1.2 dev eth0`, where the IP and eth0 device may be changed
+ Add `@reboot sudo ip link set eth0 up`
+ Add `@reboot sudo ip route add 12.0.1.0/24 dev eth0 protocol kernel src 12.0.1.2`
+ Try deactivating `GRUB` or set the timeout to a low value to reduce booting time

31 changes: 31 additions & 0 deletions examples/qemu_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env python3

from cohydra import ArgumentParser, Scenario, Network, DockerNode, QEMUNode, SwitchNode

def main():
scenario = Scenario()

net = Network("10.0.0.0", "255.255.255.0")

node1 = DockerNode('pong', docker_build_dir='./docker/pong')
node2 = QEMUNode('lubuntu', password='8559', ip='12.0.5.2', image_path='/home/julian/Master/GIT/hector_iot_testing_framework/VMs/lubuntu.qcow2',
username='lubuntu', system='qemu-system-x86_64', guest_interface='ens3', mac_address='52:54:00:12:34:56', qemu_options='-m 512 -enable-kvm')
#node3 = QEMUNode('rasp', password='raspberry', ip='12.0.6.2', image_path='/home/julian/Master/GIT/hector_iot_testing_framework/VMs/raspbian-stretch-lite.qcow',
# username='pi', system='qemu-system-arm', guest_interface='eth0', mac_address='52:54:00:12:34:55',
# qemu_options='-M versatilepb -kernel /home/julian/Master/GIT/hector_iot_testing_framework/QEMU/kernel-qemu-4.14.79-stretch -append \"root=/dev/sda2 panic=1 rootfstype=ext4 rw\" -dtb /home/julian/Master/GIT/hector_iot_testing_framework/QEMU/versatile-pb.dtb -cpu arm1176 -m 256 -machine versatilepb')
switch = SwitchNode('switch-1')

net.connect(node1, switch, delay='40ms')
net.connect(node2, switch, delay='30ms')
#net.connect(node3, switch, delay='20ms')

scenario.add_network(net)

with scenario as sim:
# To simulate forever, just do not specifiy the simulation_time parameter.
sim.simulate() #simulation_time=180


if __name__ == "__main__":
parser = ArgumentParser()
parser.run(main)
2 changes: 1 addition & 1 deletion marvis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

from .channel import Channel, CSMAChannel, WiFiChannel
from .network import Network
from .node import Node, SwitchNode, DockerNode, LXDNode, ExternalNode, SSHNode, InterfaceNode
from .node import Node, SwitchNode, DockerNode, LXDNode, ExternalNode, SSHNode, InterfaceNode, QEMUNode
from .scenario import Scenario
from .argparse import ArgumentParser
2 changes: 1 addition & 1 deletion marvis/channel/csma.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self, network, nodes, delay="0ms", speed="100Mbps"):
netmask = network.network.prefixlen
address = ipaddress.ip_interface(f'{ip_address}/{netmask}')

interface = Interface(node=node, ns3_device=ns3_device, address=address)
interface = Interface(node=node, ns3_device=ns3_device, address=address, mac_address=node.get_custom_mac())
ns3_device.SetAddress(ns_net.Mac48Address(interface.mac_address))
node.add_interface(interface)
self.interfaces.append(interface)
Expand Down
2 changes: 1 addition & 1 deletion marvis/channel/wifi.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def __init__(self, network, nodes, frequency=None, channel=1, channel_width=40,
netmask = network.network.prefixlen
address = ipaddress.ip_interface(f'{ip_address}/{netmask}')

interface = Interface(node=node, ns3_device=ns3_device, address=address)
interface = Interface(node=node, ns3_device=ns3_device, address=address, mac_address=node.get_custom_mac())
ns3_device.GetMac().SetAddress(ns_net.Mac48Address(interface.mac_address))
node.add_interface(interface)
self.interfaces.append(interface)
Expand Down
7 changes: 6 additions & 1 deletion marvis/command_executor/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class LocalCommandExecutor(CommandExecutor):
def __init__(self, name=None):
super().__init__(name)

def execute(self, command, user=None, shell=None, stdout_logfile=None, stderr_logfile=None):
def execute(self, command, user=None, shell=None, stdout_logfile=None, stderr_logfile=None, universal_newlines=None):
if user is not None:
raise ValueError('LocalCommandExecutor does not implement user argument')
if stdout_logfile is not None or stderr_logfile is not None:
Expand All @@ -36,11 +36,15 @@ def execute(self, command, user=None, shell=None, stdout_logfile=None, stderr_lo
process = subprocess.Popen( # pylint: disable=subprocess-run-check
command,
shell=False if shell is None else shell,
universal_newlines=False if universal_newlines is None else universal_newlines,
encoding='utf8',
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result = ''
for line in iter(process.stdout.readline, ''):
result += line.rstrip()
out_thread = threading.Thread(target=log_file, args=(logger, logging.INFO, process.stdout, stdout_logfile))
err_thread = threading.Thread(target=log_file, args=(logger, logging.ERROR, process.stderr, stderr_logfile))

Expand All @@ -53,3 +57,4 @@ def execute(self, command, user=None, shell=None, stdout_logfile=None, stderr_lo

if code != 0:
raise ExitCode(code, command)
return result
13 changes: 13 additions & 0 deletions marvis/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,16 @@ def setup_veth_container_end(self, ifname):
index = ipr.link_lookup(ifname=ifname)[0]
ipr.addr('add', index=index, address=str(self.address.ip), mask=self.address.network.prefixlen)
ipr.link('set', index=index, state='up')

def setup_qemu_host_address(self, address):
"""Setup the management IP address of the host to the QEMU VM.

Parameters
----------
ifname : str
The interface name on the conthost machine.
address : str
The address to communicate with the guest VM.
"""
ipr = IPRoute()
ipr.addr('add', index=ipr.link_lookup(ifname=self.bridge_name)[0], address=address, mask=24)
1 change: 1 addition & 0 deletions marvis/node/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .external import ExternalNode
from .interface import InterfaceNode
from .ssh import SSHNode
from .qemu import QEMUNode
3 changes: 3 additions & 0 deletions marvis/node/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ def wants_ip_stack(self):
:code:`True` indicates that a ns-3 IP stack shall be installed when preparing this node.
"""
raise NotImplementedError

def get_custom_mac(self):
return None

def execute_command(self, command, user=None):
"""Execute a command within the node.
Expand Down
Loading