Commit e6fe524e authored by Lu Xu's avatar Lu Xu 👀

software/ors-amarisoft: software management WIP

parent 8244c24b
......@@ -16,7 +16,7 @@
[template]
filename = instance.cfg
md5sum = 2735363970f6c5569431325c4738896f
md5sum = 92eeac0ab1231259f91a2447211c9014
[amarisoft-stats.jinja2.py]
_update_hash_filename_ = amarisoft-stats.jinja2.py
......@@ -26,17 +26,25 @@ md5sum = 6e0a052bd0ca08cc0c7b4880d3deffcc
_update_hash_filename_ = amarisoft-rf-info.jinja2.py
md5sum = c930c28365c685a6066f382c9b5d8893
[ncclient_common]
_update_hash_filename_ = ncclient_common.py
md5sum = 8f8b4a45e300f2caa3a5261d47a88639
[lopcomm-rrh-stats.jinja2.py]
_update_hash_filename_ = lopcomm-rrh-stats.jinja2.py
md5sum = b35d43ea8f8add56d7649f2862ddffc5
md5sum = 5b30c3706522d1d2040420fc4bb35ccc
[lopcomm-rrh-config.jinja2.py]
_update_hash_filename_ = lopcomm-rrh-config.jinja2.py
md5sum = b34fe47a73890097fbc6ea6374aeb38d
md5sum = 20764fa7554d90d040f3847a9573ef93
[lopcomm-rrh-software.jinja2.py]
_update_hash_filename_ = lopcomm-rrh-software.jinja2.py
md5sum = 1dab59eec5b5df9869ea16d2c68e85b0
[template-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg
md5sum = 3f7416ff8d012de487479e0372c515a3
md5sum = dbe9dd2e7b0d84bcef3a3c7f217d9d6d
[template-gnb]
_update_hash_filename_ = instance-gnb.jinja2.cfg
......@@ -84,7 +92,7 @@ md5sum = 48b577daa5b53c2cf7fe2d30ea9c0235
[dnsmasq.jinja2.cfg]
filename = config/dnsmasq.jinja2.cfg
md5sum = dfcd394d1d4b4dd1caa43a9ee7bd6080
md5sum = b6431a14a726b4e72659c87fcf26a61c
[ims.jinja2.cfg]
filename = config/ims.jinja2.cfg
......
......@@ -5,6 +5,7 @@ dhcp-range=::1,::1,constructor:{{ slap_configuration.get('tap-name', '') }},ra-n
dhcp-host={{ slapparameter_dict.get('rrh_mac_addr', '00:0a:00:00:10:20') }},[{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}]
dhcp-option=option6:dns-server,[{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) + 1 }}],[{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) + 2 }}]
dhcp-option=option6:domain-search,bbu.local
dhcp-option=option6:17,[{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-addr', '')) }}]
log-queries
log-dhcp
log-facility={{ directory['home'] }}/var/log/dnsmasq.log
......
......@@ -112,6 +112,12 @@
"type": "number",
"default": -20
},
"user-authorized-key": {
"title": "User Authorized Key",
"description": "SSH public key in order to connect to the SSH server of this instance.",
"textarea": true,
"type": "string"
},
{%- endif %}
"log_phy_debug": {
"title": "Physical layer log debug",
......
......@@ -11,11 +11,15 @@ parts =
lopcomm-firmware-dl
lopcomm-rrh-stats-service
lopcomm-rrh-config-template
lopcomm-rrh-software-template
lopcomm-cu-config
sshd-service
sshd-add-authorized-key
sshd-promise
check-lopcomm-vswr.py
check-lopcomm-pa-current.py
check-lopcomm-pa-output-power.py
check-lopcomm-lof.py
# check-lopcomm-lof.py
check-lopcomm-rssi.py
check-lopcomm-sync.py
check-lopcomm-config-log.py
......@@ -213,7 +217,7 @@ context =
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
raw python_path {{ buildout_directory }}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py
......@@ -226,6 +230,7 @@ json-log-output = ${directory:var}/log/lopcomm-rrh-stats.json.log
cfg-json-log-output = ${directory:var}/log/lopcomm-rrh-config.json.log
supervision-json-log-output = ${directory:var}/log/lopcomm-rrh-supervision.json.log
ncsession-json-log-output = ${directory:var}/log/lopcomm-rrh-ncsession.json.log
software-json-log-output = ${directory:var}/log/lopcomm-rrh-software.json.log
context =
section directory directory
section slap_configuration slap-configuration
......@@ -235,8 +240,10 @@ context =
key cfg_json_log_file :cfg-json-log-output
key supervision_json_log_file :supervision-json-log-output
key ncsession_json_log_file :ncsession-json-log-output
key software_json_log_file :software-json-log-output
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
raw python_path {{ buildout_directory }}/bin/pythonwitheggs
raw buildout_directory_path {{ buildout_directory }}
import netaddr netaddr
mode = 0775
url = {{ lopcomm_rrh_stats_template }}
......@@ -261,7 +268,8 @@ context =
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
raw python_path {{ buildout_directory }}/bin/pythonwitheggs
raw buildout_directory_path {{ buildout_directory }}
raw CreateProcessingEle_template {{ CreateProcessingEle_template }}
key cu_config_template lopcomm-cu-config:output
import netaddr netaddr
......@@ -269,6 +277,26 @@ mode = 0775
url = {{ lopcomm_rrh_config_template }}
output = ${directory:script}/lopcomm-rrh-config.py
[lopcomm-rrh-software-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/lopcomm-rrh-software.log
software-reply-json-log-output = ${directory:var}/log/lopcomm-rrh-software-reply.json.log
context =
section directory directory
section slap_configuration slap-configuration
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
key software_reply_json_log_file :software-reply-json-log-output
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory }}/bin/pythonwitheggs
raw buildout_directory_path {{ buildout_directory }}
raw firmware_name ${lopcomm-firmware-dl:filename}
import netaddr netaddr
mode = 0775
url = {{ lopcomm_rrh_software_template }}
output = ${directory:script}/lopcomm-rrh-software.py
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
......@@ -295,12 +323,71 @@ hash-files =
[lopcomm-firmware-dl]
recipe = slapos.recipe.build:download
url = https://lab.nexedi.com/nexedi/ors-utils/raw/master/lopcomm-firmware/PR.PRM61C70V1002.K010927.tar.gz
filename = PR.PRM61C70V1002.K010927.tar.gz
md5sum = 677c96f11031b7137606b9789f781c35
url = https://lab.nexedi.com/nexedi/ors-utils/raw/master/lopcomm-firmware/PR.PRM61C70V1003.tar.gz
filename = PR.PRM61C70V1003.tar.gz
md5sum = 0d07cf68b1364df4e160ed7d2a45d272
destination = ${directory:etc}/${:filename}
offline = false
[user-info]
recipe = slapos.cookbook:userinfo
# Deploy openssh-server
[sshd-port]
recipe = slapos.cookbook:free_port
minimum = 22222
maximum = 22231
ip = ${slap-configuration:ipv6-random}
[sshd-config]
recipe = slapos.recipe.template:jinja2
output = ${directory:etc}/sshd.conf
path_pid = ${directory:run}/sshd.pid
inline =
PidFile ${:path_pid}
Port ${sshd-port:port}
ListenAddress ${slap-configuration:ipv6-random}
Protocol 2
HostKey ${sshd-ssh-host-rsa-key:output}
HostKey ${sshd-ssh-host-ecdsa-key:output}
PasswordAuthentication yes
PubkeyAuthentication yes
HostKeyAlgorithms ssh-rsa,ssh-dss,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521
AuthorizedKeysFile ${buildout:directory}/.ssh/authorized_keys
Subsystem sftp {{ openssh_location }}/libexec/sftp-server
[sshd-service]
recipe = slapos.cookbook:wrapper
command-line = {{ openssh_location }}/sbin/sshd -D -e -f ${sshd-config:output}
wrapper-path = ${directory:service}/sshd
hash-files = ${sshd-config:output}
environment =
HOME=${directory:home}
[sshd-add-authorized-key]
recipe = slapos.cookbook:dropbear.add_authorized_key
home = ${buildout:directory}
key = {{ slapparameter_dict.get("user-authorized-key", '') }}
[sshd-ssh-keygen-base]
recipe = plone.recipe.command
output = ${directory:etc}/${:_buildout_section_name_}
command = {{ openssh_output_keygen }} -f ${:output} -N '' ${:extra-args}
[sshd-ssh-host-rsa-key]
<=sshd-ssh-keygen-base
extra-args=-t rsa
[sshd-ssh-host-ecdsa-key]
<=sshd-ssh-keygen-base
extra-args=-t ecdsa -b 521
[sshd-promise]
<= monitor-promise-base
promise = check_socket_listening
name = sshd.py
config-host = ${slap-configuration:ipv6-random}
config-port = ${sshd-port:port}
[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
......@@ -347,6 +434,8 @@ current-tx-gain = {{ ors_version['current-tx-gain'] }}
current-rx-gain = {{ ors_version['current-rx-gain'] }}
current-earfcn = {{ ors_version['current-earfcn'] }}
monitor-gadget-url = ${:monitor-base-url}/gadget/software.cfg.html
ssh-command = ssh ${user-info:pw-name}@${slap-configuration:ipv6-random} -p ${sshd-port:port}
ssh-url = ssh://${user-info:pw-name}@[${slap-configuration:ipv6-random}]:${sshd-port:port}
[monitor-instance-parameter]
{% if slapparameter_dict.get("name", None) %}
......
......@@ -111,6 +111,12 @@
"type": "number",
"default": -20
},
"user-authorized-key": {
"title": "User Authorized Key",
"description": "SSH public key in order to connect to the SSH server of this instance.",
"textarea": true,
"type": "string"
},
"log_phy_debug": {
"title": "Physical layer log debug",
"description": "Enable debug mode for physical layer logs",
......
......@@ -210,6 +210,7 @@ extra-context =
raw amarisoft_rf_info_template ${amarisoft-rf-info.jinja2.py:target}
raw lopcomm_rrh_stats_template ${lopcomm-rrh-stats.jinja2.py:target}
raw lopcomm_rrh_config_template ${lopcomm-rrh-config.jinja2.py:target}
raw lopcomm_rrh_software_template ${lopcomm-rrh-software.jinja2.py:target}
raw CreateProcessingEle_template ${CreateProcessingEle.jinja2.xml:target}
raw cu_config_template ${cu_config.jinja2.xml:target}
raw openssl_location ${openssl:location}
......@@ -221,6 +222,8 @@ extra-context =
raw dnsmasq_template ${dnsmasq.jinja2.cfg:target}
raw dnsmasq_location ${dnsmasq:location}
key dnsmasq_config_path dnsmasq-config:output
raw openssh_location ${openssh:location}
raw openssh_output_keygen ${openssh-output:keygen}
[dynamic-template-gnb]
< = jinja2-template-base
......@@ -323,4 +326,3 @@ context =
section directory directory
section slap_configuration slap-configuration
key slapparameter_dict slap-configuration:configuration
#!{{ python_path }}
import logging
import time
import xmltodict
from logging.handlers import RotatingFileHandler
from ncclient import manager
from ncclient.operations import RPCError
from ncclient.xml_ import *
from ncclient.devices.default import DefaultDeviceHandler
class LopcommNetconfClient:
def __init__(self):
log_file = "{{ log_file }}"
self.logger = logging.getLogger('logger')
self.logger.setLevel(logging.DEBUG)
handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=5)
self.logger.addHandler(handler)
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
handler.setFormatter(formatter)
if {{ testing }}:
return
def connect(self, host, port, user, password):
if {{ testing }}:
return
self.address = (host, port)
self.logger.info('Connecting to %s, user %s...' % (self.address, user))
self.conn = manager.connect(host=host,
port=port,
username=user,
password=password,
timeout=1800,
device_params={
'name': 'default'
},
hostkey_verify=False)
self.logger.info('Connection to %s successful' % (self.address,))
def edit_config(self, config_files):
for config_file in config_files:
with open(config_file) as f:
config_xml = f.read()
try:
self.logger.info('Sending edit-config RPC request...')
self.conn.edit_config(target='running', config=config_xml)
self.logger.info('Edit-config RPC request sent successfully')
except RPCError as e:
self.logger.error('Error sending edit-config RPC request: %s' % e)
def close(self):
# Close not compatible between ncclient and netconf server
#self.conn.close()
pass
import sys
sys.path.append({{ repr(buildout_directory_path) }})
from ncclient_common import LopcommNetconfClient
if __name__ == '__main__':
nc = LopcommNetconfClient()
nc = LopcommNetconfClient(log_file="{{ log_file }}")
while True:
try:
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword")
nc.connect("fe80::20a:ff:fe00:1020%slaptap6", 830, "oranuser", "oranpassword")
#nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword")
nc.edit_config(["{{ CreateProcessingEle_template }}", "{{ cu_config_template }}"])
break
except Exception as e:
......
#!{{ python_path }}
import time
import xmltodict
import sys
sys.path.append({{ repr(buildout_directory_path) }})
from ncclient_common import LopcommNetconfClient
if __name__ == '__main__':
nc = LopcommNetconfClient(
log_file="{{ log_file }}",
software_reply_json_log_file="{{ software_reply_json_log_file }}"
)
while True:
try:
nc.connect("fe80::20a:ff:fe00:1020%slaptap6", 830, "oranuser", "oranpassword")
# Fetch software inventory
inventory_vars = nc.get_inventory()
nonrunning_slot_name = inventory_vars["nonrunning_slot_name"]
running_slot_name = inventory_vars["running_slot_name"]
active_nonrunning_slot_name = inventory_vars["active_nonrunning_slot_name"]
nonrunning_slot_name_build_version = inventory_vars["nonrunning_slot_name_build_version"]
running_slot_name_build_version = inventory_vars["running_slot_name_build_version"]
if running_slot_name and nonrunning_slot_name:
if running_slot_name:
nc.logger.info("One slot is running and one is non-running. Proceeding...")
if running_slot_name_build_version in "{{ firmware_name }}":
nc.logger.info("Running slot's build-version %s is already updated. Skipping install." % running_slot_name_build_version)
else:
nc.logger.info("Current build version: %s" % running_slot_name_build_version)
download_rpc_xml = f"""
<software-download xmlns="urn:o-ran:software-management:1.0">
<remote-file-path>sftp://test@[2a11:9ac1:6::1]:/home/test/{{ firmware_name }}</remote-file-path>
<password>
<password>123456</password>
</password>
</software-download>
"""
download_reply_xml = nc.custom_rpc_request(download_rpc_xml)
if download_reply_xml:
download_data = xmltodict.parse(download_reply_xml)
nc.software_reply_json_logger.info('', extra={'data': download_data})
install_rpc_xml = f"""
<software-install xmlns="urn:o-ran:software-management:1.0">
<slot-name>{nonrunning_slot_name}</slot-name>
<file-names>{{ firmware_name }}</file-names>
</software-install>
"""
install_reply_xml = nc.custom_rpc_request(install_rpc_xml)
if install_reply_xml:
install_data = xmltodict.parse(install_reply_xml)
nc.software_reply_json_logger.info('', extra={'data': install_data})
activate_rpc_xml = f"""
<software-activate xmlns="urn:o-ran:software-management:1.0">
<slot-name>{nonrunning_slot_name}</slot-name>
</software-activate>
"""
activate_reply_xml = nc.custom_rpc_request(activate_rpc_xml)
if activate_reply_xml:
activate_data = xmltodict.parse(activate_reply_xml)
nc.software_reply_json_logger.info('', extra={'data': activate_data})
nc.get_inventory()
if nonrunning_slot_name_build_version in "{{ firmware_name }}" and active_nonrunning_slot_name:
nc.logger.info("Active non-running slot has the updated build version. Resetting device.")
nc.reset_device()
break
except Exception as e:
nc.logger.debug('Got exception, waiting 10 seconds before reconnecting...')
nc.logger.debug(str(e))
time.sleep(10)
finally:
nc.close()
#!{{ python_path }}
import json
import logging
import time
import xmltodict
from logging.handlers import RotatingFileHandler
from ncclient import manager
from ncclient.xml_ import *
from ncclient.devices.default import DefaultDeviceHandler
class LopcommNetconfClient:
def __init__(self):
log_file = "{{ log_file }}"
json_log_file = "{{ json_log_file }}"
cfg_json_log_file = "{{ cfg_json_log_file }}"
supervision_json_log_file = "{{ supervision_json_log_file }}"
ncsession_json_log_file = "{{ ncsession_json_log_file }}"
self.logger = logging.getLogger('logger')
self.json_logger = logging.getLogger('json_logger')
self.cfg_json_logger = logging.getLogger('cfg_json_logger')
self.supervision_json_logger = logging.getLogger('supervision_json_logger')
self.ncsession_json_logger = logging.getLogger('ncsession_json_logger')
self.logger.setLevel(logging.DEBUG)
self.json_logger.setLevel(logging.DEBUG)
self.cfg_json_logger.setLevel(logging.DEBUG)
self.supervision_json_logger.setLevel(logging.DEBUG)
self.ncsession_json_logger.setLevel(logging.DEBUG)
json_handler = RotatingFileHandler(json_log_file, maxBytes=100000, backupCount=5)
json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
json_handler.setFormatter(json_formatter)
self.json_logger.addHandler(json_handler)
cfg_json_handler = RotatingFileHandler(cfg_json_log_file, maxBytes=100000, backupCount=5)
cfg_json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
cfg_json_handler.setFormatter(cfg_json_formatter)
self.cfg_json_logger.addHandler(cfg_json_handler)
supervision_json_handler = RotatingFileHandler(supervision_json_log_file, maxBytes=100000, backupCount=5)
supervision_json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
supervision_json_handler.setFormatter(supervision_json_formatter)
self.supervision_json_logger.addHandler(supervision_json_handler)
ncsession_json_handler = RotatingFileHandler(ncsession_json_log_file, maxBytes=100000, backupCount=5)
ncsession_json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
ncsession_json_handler.setFormatter(ncsession_json_formatter)
self.ncsession_json_logger.addHandler(ncsession_json_handler)
handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=5)
self.logger.addHandler(handler)
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
handler.setFormatter(formatter)
if {{ testing }}:
return
def connect(self, host, port, user, password):
if {{ testing }}:
return
self.address = (host, port)
self.logger.info('Connecting to %s, user %s...' % (self.address, user))
self.conn = manager.connect(host=host,
port=port,
username=user,
password=password,
timeout=1800,
device_params={
'name': 'default'
},
hostkey_verify=False)
self.logger.info('Connection to %s successful' % (self.address,))
def subscribe(self):
# Filter not compatible between ncclient and netconf server
#result = self.conn.create_subscription(filter=('xpath', '/o-ran-fm:*'))
sub = self.conn.create_subscription()
self.logger.info('Subscription to %s successful' % (self.address,))
def get_notification(self):
result = None
while result == None:
self.logger.debug('Waiting for notification from %s...' % (self.address,))
result = self.conn.take_notification(block=True)
if result:
self.logger.debug('Got new notification from %s...' % (self.address,))
result_in_xml = result._raw
data_dict = xmltodict.parse(result_in_xml)
if 'alarm-notif' in data_dict['notification']:
self.json_logger.info('', extra={'data': data_dict})
elif 'supervision-notification' in data_dict['notification']:
self.supervision_json_logger.info('', extra={'data': data_dict})
elif 'netconf-session-start' in data_dict['notification'] or 'netconf-session-end' in data_dict['notification']:
self.ncsession_json_logger.info('', extra={'data': data_dict})
else:
self.cfg_json_logger.info('', extra={'data': data_dict})
def close(self):
# Close not compatible between ncclient and netconf server
#self.conn.close()
pass
import sys
sys.path.append({{ repr(buildout_directory_path) }})
from ncclient_common import LopcommNetconfClient
if __name__ == '__main__':
nc = LopcommNetconfClient()
nc = LopcommNetconfClient(
log_file="{{ log_file }}",
json_log_file="{{ json_log_file }}",
cfg_json_log_file="{{ cfg_json_log_file }}",
supervision_json_log_file="{{ supervision_json_log_file }}",
ncsession_json_log_file="{{ ncsession_json_log_file }}",
software_json_log_file="{{ software_json_log_file }}"
)
while True:
try:
nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword")
nc.connect("fe80::20a:ff:fe00:1020%slaptap6", 830, "oranuser", "oranpassword")
# nc.connect("{{ netaddr.IPAddress(slap_configuration.get('tap-ipv6-gateway', '')) }}", 830, "oranuser", "oranpassword")
nc.subscribe()
while True:
nc.get_notification()
......
This diff is collapsed.
<software-activate xmlns="urn:o-ran:software-management:1.0">
<slot-name>slot-1</slot-name>
</software-activate>
\ No newline at end of file
<software-download xmlns="urn:o-ran:software-management:1.0">
<remote-file-path>sftp://test@10.75.35.7/PR.PR46Q1LNV1001.1.tar.gz</remote-file-path>
<password>
<password>123456</password>
</password>
</software-download>
\ No newline at end of file
<software-install xmlns="urn:o-ran:software-management:1.0">
<slot-name>slot-1</slot-name>
<file-names>PR.PR46Q1LNV1001.1.tar.gz</file-names>
</software-install>
\ No newline at end of file
<software-activate xmlns="urn:o-ran:software-management:1.0">
<slot-name>slot-1</slot-name>
</software-activate>
\ No newline at end of file
......@@ -13,11 +13,13 @@ extends =
../../component/pygolang/buildout.cfg
../../component/git/buildout.cfg
../../component/dnsmasq/buildout.cfg
../../component/openssh/buildout.cfg
parts +=
template
slapos-cookbook
ltelogs.jinja2.sh
ncclient_common
# copy all configs by default
mme.jinja2.cfg
dnsmasq.jinja2.cfg
......@@ -69,6 +71,10 @@ output = ${buildout:directory}/template.cfg
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_update_hash_filename_}
[ncclient_common]
<= download-base
destination = ${buildout:directory}/ncclient_common.py
[amarisoft-stats.jinja2.py]
<= download-base
......@@ -81,6 +87,9 @@ url = ${:_profile_base_location_}/${:_update_hash_filename_}
[lopcomm-rrh-config.jinja2.py]
<= download-base
[lopcomm-rrh-software.jinja2.py]
<= download-base
[template-enb]
<= download-base
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment