Compare commits

..

No commits in common. 'main' and 'c2ef36c5a7a31ea39f51c91c7ba76430f0aaa048' have entirely different histories.

1
.gitignore vendored

@ -1,3 +1,2 @@
__pycache__
dist/
.venv

@ -333,8 +333,6 @@ cdr_finalize=UPDATE users SET inuse=(CASE WHEN inuse>0 THEN inuse-1 ELSE 0 END)
Just choose one of your RFPs as your OMM.
Find out the MAC address and fill it in the according fields in the kea config before.
To be able to have secure sip connections between the OMM and yate, generate a 16-characters long hexadecimal sip-secret.
Create a privileged user account and add the credentials to `fieldpoc_config.json`:
```
@ -343,7 +341,6 @@ Create a privileged user account and add the credentials to `fieldpoc_config.jso
"host": "10.222.222.11",
"username": "omm",
"password": "<password>"
"sipsecret": "<secret>"
}
}
```

@ -42,7 +42,7 @@ class Config:
return self.dect.check()
class ExtensionConfig(ConfigBase):
class ExtensionConfig:
def __init__(self, c):
self.num = c[0]
self._c = c[1]

@ -41,16 +41,12 @@ class Controller:
if data == "help":
self.request.sendall("""Availiable commands:
help Show this info
handlers Show currently running handlers
sync Start syncing
queues Show queue stats
reload Reload extension config file
claim <ext> <token> claim dect extension
exit Disconnect
help Show this info
handlers Show currently running handlers
sync Start syncing
queues Show queue stats
exit Disconnect
""".encode("utf-8"))
elif data == "":
continue
elif data == "quit" or data == "exit":
self.request.sendall("disconnecting\n".encode("utf-8"))
return
@ -60,14 +56,6 @@ exit Disconnect
self.fp.queue_all({"type": "sync"})
elif data == "queues":
self.request.sendall(("\n".join(["{} {}".format(name, queue.qsize()) for name, queue in self.fp.queues.items()]) + "\n").encode("utf-8"))
elif data == "reload":
self.fp.reload_config()
elif data.startswith("claim"):
data = data.split(" ")
if len(data) == 3:
self.fp.queue_all({"type": "claim", "extension": data[1], "token": data[2]})
else:
self.request.sendall("error: You have to specify calling extension and token\n".encode("utf-8"))
else:
self.request.sendall("Unknown command, type 'help'\n".encode("utf-8"))

@ -3,7 +3,6 @@
import logging
import mitel_ommclient2
import time
import hashlib
logger = logging.getLogger("fieldpoc.dect")
@ -19,46 +18,23 @@ class Dect:
ommsync=True,
)
@property
def temp_num_prefix(self):
return next(self.fp.extensions.extensions_by_type("temp")).num
def load_temp_extensions(self):
current_temp_extension = 0
used_temp_extensions = self.c.find_users(lambda u: u.num.startswith(self.temp_num_prefix))
for u in used_temp_extensions:
temp_num = u.num
self.fp.temp_extensions[temp_num] = {
"name": f"Temp {temp_num[4:]}",
"type": "dect",
"trunk": False,
"dialout_allowed": False,
}
def get_temp_number(self):
temp_num_prefix = next(self.fp.extensions.extensions_by_type("temp")).num
current_temp_extension = 0
used_temp_extensions = [num[len(self.temp_num_prefix):] for num, ext in self.fp.temp_extensions.items()]
used_temp_extensions = [u.num[len(temp_num_prefix):] for u in self.c.find_users(lambda u: u.num.startswith(temp_num_prefix))]
while "{:0>4}".format(current_temp_extension) in used_temp_extensions:
current_temp_extension += 1
return "{}{:0>4}".format(self.temp_num_prefix, current_temp_extension)
return "{}{:0>4}".format(temp_num_prefix, current_temp_extension)
def get_sip_password_for_number(self, num):
return hashlib.sha256(bytes.fromhex((self.fp.config.dect.sipsecret + str(num))[-16:])).hexdigest()[:16]
return num
def create_and_bind_user(self, d, num):
u = self.c.create_user(num)
self.c.attach_user_device(u.uid, d.ppn)
self.c.set_user_relation_fixed(u.uid)
self.c.set_user_sipauth(u.uid, num, self.get_sip_password_for_number(num))
return u
def run(self):
logger.info("initialising connection to OMM")
self._init_client()
self.load_temp_extensions()
while True:
msg = self.fp.queues["dect"].get()
@ -67,90 +43,13 @@ class Dect:
if msg.get("type") == "stop":
break
elif msg.get("type") == "sync":
logger.info("syncing")
extensions = self.fp.extensions.extensions_by_type("dect")
extensions_by_num = {e.num: e for e in extensions}
extensions_by_ipei = {e._c['dect_ipei']: e for _, e in extensions_by_num.items() if e._c.get('dect_ipei')}
created_tmp_ext = False
users_by_ext = {}
users_by_uid = {}
for user in self.c.get_users():
e = extensions_by_num.get(user.num)
if not e:
# user in omm, but not as dect in nerd
if user.num.startswith(next(self.fp.extensions.extensions_by_type("temp")).num):
users_by_ext[user.num] = user
users_by_uid[user.uid] = user
else:
pass
# TODO: delete in omm
continue
elif e._c['name'] != user.name:
self.c.set_user_name(user.uid, e._c['name'])
if e._c.get('dect_ipei') and user.relType != mitel_ommclient2.types.PPRelTypeType("Unbound"):
d = self.c.get_device(user.ppn)
if d.ipei != e._c['dect_ipei']:
logger.debug(f"Detaching {user} {d}")
self.c.detach_user_device(user.uid, user.ppn)
self.c.set_user_sipauth(user.uid, e.num, self.get_sip_password_for_number(e.num))
users_by_ext[user.num] = user
users_by_uid[user.uid] = user
for d in self.c.get_devices():
e = extensions_by_ipei.get(d.ipei)
if e:
# device is in nerd
u = users_by_ext.get(e.num)
if u and d.relType == mitel_ommclient2.types.PPRelTypeType("Unbound"):
logger.debug(f'Binding user for {d}')
self.c.attach_user_device(u.uid, d.ppn)
self.c.set_user_relation_fixed(u.uid)
elif d.relType != mitel_ommclient2.types.PPRelTypeType("Unbound"):
ui = users_by_uid.get(d.uid)
if ui.num != e.num:
logger.debug(f'User for {d} has wrong number')
if self.fp.temp_extensions.get(ui.num):
self.fp.temp_extensions.pop(ui.num)
self.c.set_user_num(d.uid, e.num)
self.c.set_user_sipauth(d.uid, e.num, self.get_sip_password_for_number(e.num))
self.c.set_user_name(user.uid, e._c['name'])
else:
logger.debug(f'Creating and binding user for {d}')
user = self.create_and_bind_user(d, e.num)
self.c.set_user_name(user.uid, e._c['name'])
elif d.relType == mitel_ommclient2.types.PPRelTypeType("Unbound"):
temp_num = self.get_temp_number()
logger.debug(f'Creating and binding tmp-user for {d}: {temp_num}')
user = self.create_and_bind_user(d, temp_num)
self.c.set_user_name(user.uid, f"Temp {temp_num[4:]}")
self.fp.temp_extensions[temp_num] = {
"name": f"Temp {temp_num[4:]}",
"type": "dect",
"trunk": False,
"dialout_allowed": False,
}
created_tmp_ext = True
if created_tmp_ext:
self.fp.queues['routing'].put({"type": "sync"})
elif msg.get("type") == "claim":
e = None
for ext in self.fp.extensions.extensions_by_type("dect"):
if ext._c.get('dect_claim_token') and ext._c['dect_claim_token'] == msg.get("token")[4:]:
e = ext
break
if e:
user = next(self.c.find_users(lambda u: u.num == msg.get("extension")))
if self.fp.temp_extensions.get(user.num):
self.fp.temp_extensions.pop(user.num)
self.c.set_user_num(user.uid, e.num)
self.c.set_user_sipauth(user.uid, e.num, self.get_sip_password_for_number(e.num))
self.c.set_user_name(user.uid, e._c['name'])
self.c.connection.close()
unbound_devices = self.c.find_devices(lambda d: d.relType == mitel_ommclient2.types.PPRelTypeType("Unbound"))
for d in unbound_devices:
print(d)
temp_num = self.get_temp_number()
u = self.c.create_user(temp_num)
print(u)
self.c.attach_user_device(u.uid, d.ppn)
self.c.set_user_relation_fixed(u.uid)
self.c.set_user_sipauth(u.uid, temp_num, self.get_sip_password_for_number(temp_num))

@ -17,7 +17,6 @@ logger = logging.getLogger("fieldpoc.fieldpoc")
class FieldPOC:
config = None
extensions = None
temp_extensions = {}
def __init__(self, config_file_path, extensions_file_path):
logger.info("loading configuration")
@ -76,10 +75,6 @@ class FieldPOC:
logger.info("started components")
def reload_config(self):
self._load_extensions()
self.queue_all({"type": "sync"})
def _load_config(self):
self.config = config.Config(json.loads(self.config_file_path.read_text()))

@ -27,12 +27,11 @@ class YwsdYateModel(YateModel):
@classmethod
def create(cls, diffsync, ids, attrs):
with diffsync.engine.connect() as conn:
result = conn.execute(
conn.execute(
Yate.table.insert().values(
guru3_identifier=ids["guru3_identifier"], **attrs
)
)
attrs["yate_id"] = result.inserted_primary_key[0]
return super().create(diffsync, ids=ids, attrs=attrs)
def update(self, attrs):
@ -85,7 +84,7 @@ class YwsdExtensionModel(ExtensionModel):
@classmethod
def create(cls, diffsync, ids, attrs):
with diffsync.engine.connect() as conn:
result = conn.execute(
conn.execute(
Extension.table.insert().values(
extension=ids["extension"],
type=attrs["extension_type"],
@ -99,7 +98,6 @@ class YwsdExtensionModel(ExtensionModel):
),
)
)
attrs["extension_id"] = result.inserted_primary_key[0]
return super().create(diffsync, ids=ids, attrs=attrs)
def update(self, attrs):
@ -212,7 +210,7 @@ class YwsdForkRankModel(ForkRankModel):
@classmethod
def create(cls, diffsync, ids, attrs):
with diffsync.engine.connect() as conn:
result = conn.execute(
conn.execute(
ForkRank.table.insert().values(
extension_id=diffsync.get(
"extension", ids["extension"]
@ -221,7 +219,6 @@ class YwsdForkRankModel(ForkRankModel):
**attrs,
)
)
attrs["forkrank_id"] = result.inserted_primary_key[0]
return super().create(diffsync, ids=ids, attrs=attrs)
def update(self, attrs):
@ -278,7 +275,7 @@ class YwsdForkRankMemberModel(ForkRankMemberModel):
conn.execute(
ForkRank.member_table.update()
.where(
sqlalchemy.and_(
_and(
ForkRank.member_table.c.forkrank_id
== self.diffsync.get("forkrank", self.forkrank).forkrank_id,
ForkRank.member_table.c.extension_id
@ -293,7 +290,7 @@ class YwsdForkRankMemberModel(ForkRankMemberModel):
with self.diffsync.engine.connect() as conn:
conn.execute(
ForkRank.member_table.delete().where(
sqlalchemy.and_(
_and(
ForkRank.member_table.c.forkrank_id
== self.diffsync.get("forkrank", self.forkrank).forkrank_id,
ForkRank.member_table.c.extension_id
@ -313,15 +310,11 @@ class BackendNerd(diffsync.DiffSync):
top_level = ["yate", "extension", "user", "forkrank", "forkrankmember"]
def __init__(self, fp, *args, **kwargs):
self.fp = fp
super().__init__(*args, **kwargs)
def load(self, data):
#yate_dect = self.yate(
# guru3_identifier="dect", hostname="dect", voip_listener="local"
#)
#self.add(yate_dect)
yate_dect = self.yate(
guru3_identifier="dect", hostname="dect", voip_listener="local"
)
self.add(yate_dect)
yate_sip = self.yate(
guru3_identifier="sip", hostname="sip", voip_listener="local"
)
@ -346,8 +339,6 @@ class BackendNerd(diffsync.DiffSync):
active=True,
)
self.add(frm)
elif value["type"] in ["temp"]:
continue
extension = self.extension(
extension=key,
@ -370,7 +361,7 @@ class BackendNerd(diffsync.DiffSync):
user = self.user(
username=key,
displayname=value["name"],
password=value.get("sip_password", self.fp._dect.get_sip_password_for_number(key)),
password=value.get("sip_password", key),
user_type=user_type[value["type"]],
trunk=value["trunk"],
static_target=value.get("static_target", ""),
@ -482,8 +473,8 @@ class Routing:
def run(self):
while True:
msg = self.fp.queues["routing"].get()
self.fp.queues["routing"].task_done()
msg = self.fp.queues["dect"].get()
self.fp.queues["dect"].task_done()
if msg.get("type") == "stop":
break
@ -491,10 +482,8 @@ class Routing:
logger.info("syncing")
state_fieldpoc = BackendNerd(self.fp)
extensions = self.fp.extensions._c.copy()
extensions['extensions'].update(self.fp.temp_extensions)
state_fieldpoc.load(extensions)
state_fieldpoc = BackendNerd()
state_fieldpoc.load(self.fp.extensions._c)
state_yate = BackendYwsd()
state_yate.load("postgresql+psycopg2://{}:{}@{}/{}".format(
self.fp.config.database.username,

@ -7,7 +7,6 @@
"host": "10.222.222.11",
"username": "omm",
"password": "xxx"
"sipsecret": "51df84aace052b0e75b8c1da5a6da9e2"
},
"yate": {
"host": "127.0.0.1",

@ -95,14 +95,14 @@
"name": "Temporary Numbers",
"trunk": false,
"dialout_allowed": true,
"type": "temp"
"type": "static"
},
"9997": {
"9999": {
"name": "DECT Claim Extensions",
"type": "static",
"dialout_allowed": false,
"trunk": true,
"static_target": "external/nodata//run/current-system/sw/bin/dect_claim"
"static_target": "external/nodata//opt/nerdsync/claim.py"
}
}
}

Loading…
Cancel
Save