Skip to content

Commit

Permalink
ns-api: add ns.migration
Browse files Browse the repository at this point in the history
  • Loading branch information
gsanchietti committed Nov 3, 2023
1 parent 69c9008 commit 242b7c7
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/ns-api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ define Package/ns-api/install
$(INSTALL_DATA) ./files/ns.account.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_BIN) ./files/ns.backup $(1)/usr/libexec/rpcd/
$(INSTALL_DATA) ./files/ns.backup.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_BIN) ./files/ns.migration $(1)/usr/libexec/rpcd/
$(INSTALL_DATA) ./files/ns.migration.json $(1)/usr/share/rpcd/acl.d/
$(INSTALL_DIR) $(1)/lib/upgrade/keep.d
$(INSTALL_CONF) files/msmtp.keep $(1)/lib/upgrade/keep.d/msmtp
$(LN) /usr/bin/msmtp $(1)/usr/sbin/sendmail
Expand Down
72 changes: 72 additions & 0 deletions packages/ns-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2966,3 +2966,75 @@ Example response:
}
}
```

## ns.migration

Manage migration from NS7

### list-devices

List existing devices:
```
api-clit ns.migration list-devices
```

Response example:
```json
{
"devices": [
{
"name": "eth0",
"hwaddr": "52:54:00:6a:50:bf",
"role": null,
"ipaddr": null
},
{
"name": "eth1",
"hwaddr": "52:54:00:20:82:a6",
"role": "wan",
"ipaddr": "192.168.122.49"
}
]
}
```

### upload

Upload a NS7 migration archive:
```
api-cli ns.migration upload --data '{"archive": "H4sIAAAAAAAAA+w9a3PbNr..."}'
```

This API can return a validation error if the given file is not a valid NS7 migration export archive.

Response example:
```json
{
"devices": [
{
"name": "enp1s0",
"hwaddr": "xx:yy:xx",
"ipaddr": "1.2.3.4",
"role": "green"
},
{
"name": "en1",
"hwaddr": "xx:zz:zz",
"ipaddr": "4.5.6.7",
"role": "red"
}
]
}
```

### migrate

Execute the migration:
```
api-cli ns.migration migrate --data '{"mappings": [{"old": "52:54:00:75:1C:C1", "new": "53:54:44:75:1A:AA"}]}'
```

Response example:
```json
{"result": "success"}
```
90 changes: 90 additions & 0 deletions packages/ns-api/files/ns.migration
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/python3

#
# Copyright (C) 2023 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

import base64
import json
import os
import shutil
import subprocess
import sys
from euci import EUci

from nethsec import utils

MIGRATE_DIR = '/tmp/migration'
MIGRATE_PATH = f'{MIGRATE_DIR}/export.tar.gz'

def get_ip(device):
try:
data = json.loads(subprocess.run(["ip", "--json", "address", "show", "dev", device], capture_output=True, text=True, check=True).stdout)
if len(data) > 0:
for addr in data[0].get('addr_info', []):
if addr.get('family', '') == 'inet' and addr.get("local", None):
return addr.get("local")
except:
return None

cmd = sys.argv[1]

if cmd == 'list':
print(json.dumps({
'upload': {
'archive': 'str',
},
'list-devices': {},
'migrate': { "mappings": [{"old": "52:54:00:75:1C:C1", "new": "53:54:44:75:1A:AA"}] }
}))
elif cmd == 'call':
action = sys.argv[2]
if action == 'migrate':
try:
data = json.load(sys.stdin)
cmd = ['/usr/sbin/ns-import', MIGRATE_PATH]
for m in data['mappings']:
cmd.append("-m")
cmd.append(f'{m["old"]}={m["new"]}')
subprocess.run(cmd, check=True, capture_output=True)
# return success
print(json.dumps({'result': 'success'}))
except RuntimeError as error:
print(json.dumps(utils.generic_error(error.args[0])))

elif action == 'upload':
ret = []
try:
# read input and prepare the paths
data = json.load(sys.stdin)
if os.path.exists(MIGRATE_DIR):
shutil.rmtree(MIGRATE_DIR, ignore_errors=True)
os.makedirs(MIGRATE_DIR, exist_ok=True)
# write the archive and explode it
open(MIGRATE_PATH, 'wb').write(base64.b64decode(data['archive']))
subprocess.run(['/bin/tar', 'xzf', MIGRATE_PATH, '-C', MIGRATE_DIR], check=True)
with open(f'{MIGRATE_DIR}/export/network.json', 'r') as fp:
data = json.load(fp)
# return the list of interfaces from the archive
for i in data['interfaces']:
ret.append({"name": i.get('name'), "hwaddr": i.get('hwaddr'), "ipaddr": i.get('ipaddr'), "role": i.get("role")})
print(json.dumps({'devices': ret}))
except RuntimeError as error:
print(json.dumps(utils.generic_error(error.args[0])))

elif action == 'list-devices':
u = EUci()
ret = []
try:
data = json.loads(subprocess.run(["/sbin/ip", "--json", "link"], check=True, capture_output=True, text=True).stdout)
for i in data:
if i['ifname'] == "lo" or i['ifname'].startswith('br-') or i['ifname'].startswith('tun-') or i['ifname'].startswith('ifb-'):
continue
ret.append({'name': i['ifname'], 'hwaddr': i['address'], "role": utils.get_interface_from_device(u, i['ifname']), 'ipaddr': get_ip(i['ifname'])})

print(json.dumps({'devices': ret}))
except KeyError as error:
print(error)
print(json.dumps(utils.validation_error('passphrase', 'required')))

13 changes: 13 additions & 0 deletions packages/ns-api/files/ns.migration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"migration-manager": {
"description": "Manages migration",
"write": {},
"read": {
"ubus": {
"ns.migration": [
"*"
]
}
}
}
}

0 comments on commit 242b7c7

Please sign in to comment.