Skip to content

Commit

Permalink
More polish
Browse files Browse the repository at this point in the history
  • Loading branch information
grimadas committed Mar 9, 2020
1 parent 944bda3 commit a8dd838
Show file tree
Hide file tree
Showing 9 changed files with 3,038 additions and 377 deletions.
3,126 changes: 2,823 additions & 303 deletions Intro.ipynb

Large diffs are not rendered by default.

163 changes: 108 additions & 55 deletions p2psimpy/config.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import inspect
from dataclasses import dataclass, field
import yaml

import scipy.stats
from ast import literal_eval as make_tuple
from random import choices
from typing import Any

import attr

class Dist:

class Yamlable:

@classmethod
def load(cls, yaml_file):
cls(*(load_config_parameter(config[field.name]) for field in fields(cls)))
def __init__(self, name: str, params):
self.name = name
self.params = params

def save(self, yaml_file):
pass
def to_repr(self):
return {'name': self.name, 'params': str(self.params)}

@classmethod
def from_repr(cls, yaml_dict):
return cls(**yaml_dict)

@dataclass
class Dist:
name: str = 'sample'
params: Any = ('Other', 'Ohio')
# def __str__(self):
# return str({'Dist': {'name': self.name, 'params': str(self.params)}})

def generate(self, n=1):
"""
Expand All @@ -33,6 +30,8 @@ def generate(self, n=1):
weights = self.params['weights'] if 'weights' in self.params else None
values = self.params['values'] if 'values' in self.params \
else self.params
weights = make_tuple(weights) if type(weights) == str else weights
values = make_tuple(values) if type(values) == str else values
res = choices(values, weights=weights, k=n)
return res if n != 1 else res[0]

Expand All @@ -45,27 +44,105 @@ def __get__(self, inst, obj):
return self.generate(1)


class Wrap:
def __init__(self, cls):
self._wrap = cls

def to_repr(self):
return {self._wrap.__class__.__name__: self._wrap.to_repr()}

@classmethod
def from_repr(cls, yaml_dict):
return cls(**yaml_dict)

def __str__(self):
return str(self._wrap)

def get(self):
return self._wrap.__get__(None, None)

def generate(self, n=1):
return self._wrap.generate(n)


class ConfigWrap:
def __init__(self, cls):
self.cls = cls

def to_repr(self):
return self.cls.repr()

def __str__(self):
return str(self.cls.repr())

def get(self):
return self.cls.get()


class Config:
@classmethod
def get_attr(cls):
return {i[0]: i[1] for i in inspect.getmembers(cls) if not i[0].startswith('_')
and not callable(i[1])}
def _serialize(cls, val):
if isinstance(val, (tuple, list, dict)):
if type(val) == dict:
return {cls._serialize(k): cls._serialize(v) for k, v in val.items()}
else:
return list(cls._serialize(k) for k in val)
else:
if type(val) == Wrap or type(val) == ConfigWrap:
return val.to_repr()
else:
return val

@classmethod
def _deserialize(cls, val):
if type(val) == dict:
if 'Dist' in val:
return Wrap(Dist(**val['Dist']))
else:
return {k: cls._deserialize(v) for k, v in val.items()}
else:
return val

@classmethod
def get_attr_repr(cls):
for i in inspect.getmembers(cls):
if not i[0].startswith('_') and not callable(i[1]):
yield cls._serialize(i)

@classmethod
def repr(cls):
root = {cls.__name__: {}}
root = dict()
root[str(cls.__name__)] = dict()
main = root[cls.__name__]
main.update(cls.get_attr())
v = cls.get_attr_repr()
main.update(cls.get_attr_repr())
return root

@classmethod
def get(cls):
def _get(cls, val):
if type(val) == dict:
return {k: cls._get(v) for k, v in val.items()}
elif isinstance(val, list):
return [cls._get(v) for v in val]
if type(val) == Wrap or type(val) == ConfigWrap:
return val.get()
else:
return val

@classmethod
def get(cls, n=1):
full_dict = dict()
for i in inspect.getmembers(cls):
if not i[0].startswith('_') and not callable(i[1]):
if type(i[1]) == Wrap or type(i[1]) == Config:
yield i[0], i[1].get()
else:
yield i[0], i[1]
full_dict[i[0]] = cls._get(i[1])
return full_dict

@classmethod
def from_repr(cls, cls_name, yaml_dict):
cls.__name__ = cls_name
cls.__qualname__ = cls_name
for k, v in cls._deserialize(yaml_dict).items():
setattr(cls, k, v)


class PeerNameGenerator:
Expand All @@ -81,28 +158,6 @@ def generate_name(self, peer_type: str):
return peer_type + "_" + str(self.peer_indexes[peer_type])


@dataclass
class ConnectionConfig:
ping_interval: int = 500
max_silence: int = 20
min_keep_time: int = 20
min_peers: int = 5
max_peers: int = 30
peer_list_number: int = 1
peer_batch_request_number: int = 3


def load_config_parameter(config):
if type(config) is dict and 'name' in config and 'parameters' in config:
return get_random_values(config)[0]
else:
return config


def load_from_config(cls, config):
return cls(*(load_config_parameter(config[field.name]) for field in fields(cls)))


class ConfigLoader:

@staticmethod
Expand All @@ -116,16 +171,14 @@ def load_latencies():
return list(yaml.safe_load_all(s))[1]


@dataclass
class DisruptionConfig:
mtbf: int # Mean time between failures = 24. * 60 * 60 # secs (mean time between failures)
availability: float # = 0.97 # Average availability of the service
interval: int # = 1. # tick interval

def load_config_from_yaml(yaml_file):
class NewConfig(Config): pass

@dataclass
class SlowdownConfig(DisruptionConfig):
slowdown: float # 0.2
with open(yaml_file) as s:
raw = yaml.load(s)
cls_name = list(raw.keys())[0]
NewConfig.from_repr(cls_name, raw[cls_name])
return NewConfig


def from_config(cls, name_gen: PeerNameGenerator, peer_type: str, config: dict):
Expand Down
4 changes: 0 additions & 4 deletions p2psimpy/network.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
from p2psimpy.utils import get_latency_delay


class Connection:

def __init__(self, sender, receiver):
""" Class to represent connection between two peers
:param locations: Map that contains the latencies between locations
"""
self.env = sender.env
self.locations = sender.sim.locations
self.get_latency = self.sender.sim.get_latency_delay

self.sender = sender
Expand Down
2 changes: 1 addition & 1 deletion p2psimpy/peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Peer:

def __init__(self, sim, name: str, location: str, bandwidth_ul: float, bandwidth_dl: float):
"""
Physical peer class
Physical representation of a Peer
:param sim: Simulation environment
:param name:
:param location:
Expand Down
8 changes: 3 additions & 5 deletions p2psimpy/peer_factory.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import logging

from p2psimpy.peer import Peer
from p2psimpy.services.connection_manager import ConnectionManager
from p2psimpy.services.disruption import Downtime, Slowdown

from p2psimpy.config import load_from_config, PeerConfig, PeerNameGenerator, ConnectionConfig, \

from p2psimpy.config import load_from_config, PeerNameGenerator, ConnectionConfig, \
SlowdownConfig, DisruptionConfig


class PeerFactory:
CLASS_MAP = {
'ConnectionManager': ConnectionManager,
'ConnectionManager': BaseConnectionManager,
'Downtime': Downtime,
'Slowdown': Slowdown
}
Expand Down
1 change: 0 additions & 1 deletion p2psimpy/services/connection_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from dataclasses import dataclass
from random import sample

from p2psimpy.logger import setup_logger
Expand Down
1 change: 0 additions & 1 deletion p2psimpy/services/disruption.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import random

from p2psimpy.config import DisruptionConfig, SlowdownConfig
from p2psimpy.peer import Peer
from p2psimpy.services.base import BaseRunner

Expand Down
Loading

0 comments on commit a8dd838

Please sign in to comment.