Skip to content

Commit 516a0cb

Browse files
committedNov 14, 2023·
session: add copy_config support
Add support for copying/importing configuration from another datastore. The equivalent of `sysrepocfg --copy-from candidate --datastore running` is: with connection.start_session("running") as s: s.copy_config("candidate") Fixes: sysrepo#55 Signed-off-by: Robin Jarry <[email protected]>
1 parent 70ad005 commit 516a0cb

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed
 

Diff for: ‎cffi/cdefs.h

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ int sr_discard_items(sr_session_ctx_t *, const char *);
192192
int sr_delete_item(sr_session_ctx_t *, const char *, const sr_edit_options_t);
193193
int sr_oper_delete_item_str(sr_session_ctx_t *, const char *, const char *, const sr_edit_options_t);
194194
int sr_edit_batch(sr_session_ctx_t *, const struct lyd_node *, const char *);
195+
int sr_copy_config(sr_session_ctx_t *, const char *, sr_datastore_t, uint32_t);
195196
int sr_replace_config(sr_session_ctx_t *, const char *, struct lyd_node *, uint32_t);
196197
int sr_validate(sr_session_ctx_t *, const char *, uint32_t);
197198
int sr_apply_changes(sr_session_ctx_t *, uint32_t);

Diff for: ‎examples/sysrepocfg.py

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ def main():
4343
output is printed to stdout.
4444
""",
4545
)
46+
group.add_argument(
47+
"-C",
48+
"--copy-from",
49+
metavar="DATASTORE",
50+
choices=("running", "startup", "operational", "candidate"),
51+
help="""
52+
Perform a copy-config from a datastore.
53+
""",
54+
)
4655
parser.add_argument(
4756
"-v", "--verbose", action="count", default=0, help="Increase verbosity."
4857
)
@@ -107,6 +116,9 @@ def main():
107116
sys.stdout, args.format, pretty=True, with_siblings=True
108117
)
109118

119+
elif args.copy_from:
120+
sess.copy_config(args.copy_from)
121+
110122
elif args.rpc:
111123
with conn.get_ly_ctx() as ctx:
112124
rpc_input = ctx.parse_data_mem(

Diff for: ‎sysrepo/session.py

+29
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,35 @@ def replace_config(
13141314
dnode = module.parse_data_dict(config, strict=strict, validate=False)
13151315
self.replace_config_ly(dnode, module_name, timeout_ms=timeout_ms)
13161316

1317+
def copy_config(
1318+
self, src_datastore: str, module_name: str = None, timeout_ms: int = 0
1319+
) -> None:
1320+
"""
1321+
Replaces a conventional datastore with the contents of another conventional
1322+
datastore. If the module is specified, limits the operation only to the
1323+
specified module. If it is not specified, the operation is performed on all
1324+
modules.
1325+
1326+
Note that copying from candidate to running or vice versa causes the candidate
1327+
datastore to revert to original behavior of mirroring running datastore
1328+
(datastores).
1329+
1330+
Required WRITE access.
1331+
1332+
:arg src_datastore:
1333+
Source datastore to copy configuration from. Can be one of `running`,
1334+
`startup`, `operational` or `candidate`. Must be a different datastore
1335+
than the session's.
1336+
:arg module_name:
1337+
Optional module name that limits the copy operation only to this module.
1338+
:arg timeout_ms:
1339+
Configuration callback timeout in milliseconds. If 0, default is used.
1340+
"""
1341+
if self.is_implicit:
1342+
raise SysrepoUnsupportedError("cannot copy config from implicit sessions")
1343+
ds = datastore_value(src_datastore)
1344+
check_call(lib.sr_copy_config, self.cdata, str2c(module_name), ds, timeout_ms)
1345+
13171346
def validate(self, module_name: str = None) -> None:
13181347
"""
13191348
Perform the validation a datastore and any changes made in the current

Diff for: ‎tests/test_session.py

+20
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,26 @@ def test_session_replace_config(self):
9191
config = {"conf": {"system": {"hostname": "foobar"}}}
9292
sess.replace_config(config, "sysrepo-example")
9393

94+
def test_session_copy_config(self):
95+
with self.conn.start_session("candidate") as sess:
96+
config = {"conf": {"system": {"hostname": "foobar"}}}
97+
sess.replace_config(config, "sysrepo-example")
98+
with self.conn.start_session("running") as sess:
99+
sess.copy_config("candidate", module_name="sysrepo-example")
100+
data = sess.get_data("/sysrepo-example:conf")
101+
self.assertEqual(data, {"conf": {"system": {"hostname": "foobar"}}})
102+
103+
def test_session_copy_config_errors(self):
104+
with self.conn.start_session("candidate") as sess:
105+
config = {"conf": {"system": {"hostname": "foobar"}}}
106+
sess.replace_config(config, "sysrepo-example")
107+
with self.conn.start_session("running") as sess:
108+
with self.assertRaises(ValueError):
109+
sess.copy_config("invalid")
110+
with self.conn.start_session("running") as sess:
111+
with self.assertRaises(sysrepo.SysrepoNotFoundError):
112+
sess.copy_config("candidate", module_name="not-found")
113+
94114
def test_session_set_item(self):
95115
def iface(name, field):
96116
return "/sysrepo-example:conf/network/interface[name=%r]/%s" % (name, field)

0 commit comments

Comments
 (0)