1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <yang/translator_subnet.h>
10 #include <yang/yang_models.h>
11 #include <yang/tests/sysrepo_setup.h>
12 
13 #include <gtest/gtest.h>
14 
15 using namespace std;
16 using namespace isc;
17 using namespace isc::data;
18 using namespace isc::yang;
19 using namespace isc::yang::test;
20 using namespace sysrepo;
21 
22 namespace {
23 
24 /// @brief Translator name.
25 extern char const subnet_list[] = "subnet list";
26 
27 /// @brief Test fixture class for @ref TranslatorSubnets.
28 class TranslatorSubnetsTestKeaV4 :
29     public GenericTranslatorTest<subnet_list, TranslatorSubnets> {
30 public:
31 
32     /// Constructor.
TranslatorSubnetsTestKeaV4()33     TranslatorSubnetsTestKeaV4() {
34         model_ = KEA_DHCP4_SERVER;
35     }
36 };
37 class TranslatorSubnetsTestKeaV6 :
38     public GenericTranslatorTest<subnet_list, TranslatorSubnets> {
39 public:
40 
41     /// Constructor.
TranslatorSubnetsTestKeaV6()42     TranslatorSubnetsTestKeaV6() {
43         model_ = KEA_DHCP6_SERVER;
44     }
45 };
46 class TranslatorSubnetsTestIetfV6 :
47     public GenericTranslatorTest<subnet_list, TranslatorSubnets> {
48 public:
49 
50     /// Constructor.
TranslatorSubnetsTestIetfV6()51     TranslatorSubnetsTestIetfV6() {
52         model_ = IETF_DHCPV6_SERVER;
53     }
54 };
55 
56 // This test verifies that an empty subnet list can be properly
57 // translated from YANG to JSON using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,getEmptyIetf)58 TEST_F(TranslatorSubnetsTestIetfV6, getEmptyIetf) {
59     // Get the subnet list and check if it is empty.
60     const string& xpath =
61         "/ietf-dhcpv6-server:server/server-config/network-ranges";
62     ConstElementPtr subnets;
63     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
64     ASSERT_FALSE(subnets);
65 }
66 
67 // This test verifies that an empty subnet list can be properly
68 // translated from YANG to JSON using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV6,getEmptyKea)69 TEST_F(TranslatorSubnetsTestKeaV6, getEmptyKea) {
70     // Get the subnet list and check if it is empty.
71     const string& xpath = "/kea-dhcp6-server:config";
72     ConstElementPtr subnets;
73     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
74     ASSERT_FALSE(subnets);
75 }
76 
77 // This test verifies that one subnet can be properly
78 // translated from YANG to JSON using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,getIetf)79 TEST_F(TranslatorSubnetsTestIetfV6, getIetf) {
80     // Create the subnet 2001:db8::/48 #111.
81     const string& xpath =
82         "/ietf-dhcpv6-server:server/server-config/network-ranges";
83     const string& xsub = xpath + "/network-range[network-range-id='111']";
84     S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
85     const string& xsubnet = xsub + "/network-prefix";
86     EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
87 
88     // Get the subnet.
89     ConstElementPtr subnet;
90     EXPECT_NO_THROW(subnet = t_obj_->getSubnet(xsub));
91     ASSERT_TRUE(subnet);
92     EXPECT_EQ("{ \"id\": 111, "
93               "\"subnet\": \"2001:db8::/48\" }",
94               subnet->str());
95 
96     // Get the subnet list and check if the subnet is in it.
97     ConstElementPtr subnets;
98     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
99     ASSERT_TRUE(subnets);
100     ASSERT_EQ(Element::list, subnets->getType());
101     ASSERT_EQ(1, subnets->size());
102     ASSERT_TRUE(subnets->get(0));
103     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
104 }
105 
106 // This test verifies that one subnet can be properly
107 // translated from YANG to JSON using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV6,getKea)108 TEST_F(TranslatorSubnetsTestKeaV6, getKea) {
109     // Create the subnet 2001:db8::/48 #111.
110     const string& xpath = "/kea-dhcp6-server:config";
111     const string& xsub = xpath + "/subnet6[id='111']";
112     S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
113     const string& xsubnet = xsub + "/subnet";
114     EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
115 
116     // Get the subnet.
117     ConstElementPtr subnet;
118     EXPECT_NO_THROW(subnet = t_obj_->getSubnet(xsub));
119     ASSERT_TRUE(subnet);
120     ElementPtr expected = Element::createMap();
121     expected->set("id", Element::create(111));
122     expected->set("subnet", Element::create(string("2001:db8::/48")));
123     EXPECT_TRUE(expected->equals(*subnet));
124 
125     // Get the subnet list and check if the subnet is in it.
126     ConstElementPtr subnets;
127     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
128     ASSERT_TRUE(subnets);
129     ASSERT_EQ(Element::list, subnets->getType());
130     ASSERT_EQ(1, subnets->size());
131     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
132 }
133 
134 // This test verifies that one subnet with two pools can be properly
135 // translated from YANG to JSON using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,getPoolsIetf)136 TEST_F(TranslatorSubnetsTestIetfV6, getPoolsIetf) {
137     // Create the subnet 2001:db8::/48 #111.
138     const string& xpath =
139         "/ietf-dhcpv6-server:server/server-config/network-ranges";
140     const string& xsub = xpath + "/network-range[network-range-id='111']";
141     S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
142     const string& xsubnet = xsub + "/network-prefix";
143     EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
144 
145     // Create the pool 2001:db8::1:0/112 #1.
146     const string& xpool = xsub + "/address-pools";
147     const string& prefix1 = xpool + "/address-pool[pool-id='1']/pool-prefix";
148     S_Val s_pool1(new Val("2001:db8::1:0/112"));
149     EXPECT_NO_THROW(sess_->set_item(prefix1.c_str(), s_pool1));
150 
151     // Create the pool 2001:db8::2:0/112 #2.
152     const string& prefix2 = xpool + "/address-pool[pool-id='2']/pool-prefix";
153     S_Val s_pool2(new Val("2001:db8::2:0/112"));
154     EXPECT_NO_THROW(sess_->set_item(prefix2.c_str(), s_pool2));
155 
156     // Get the subnet.
157     ConstElementPtr subnet;
158     EXPECT_NO_THROW(subnet = t_obj_->getSubnet(xsub));
159     ASSERT_TRUE(subnet);
160     string expected =
161         "{\n"
162         "  \"id\": 111,\n"
163         "  \"pools\": [\n"
164         "    {\n"
165         "      \"pool\": \"2001:db8::1:0/112\"\n"
166         "    },\n"
167         "    {\n"
168         "      \"pool\": \"2001:db8::2:0/112\"\n"
169         "    }\n"
170         "  ],\n"
171         "  \"subnet\": \"2001:db8::/48\"\n"
172         "}";
173     EXPECT_EQ(expected, prettyPrint(subnet));
174 
175     // Get the subnet list and check if the subnet is in it.
176     ConstElementPtr subnets;
177     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
178     ASSERT_TRUE(subnets);
179     ASSERT_EQ(Element::list, subnets->getType());
180     ASSERT_EQ(1, subnets->size());
181     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
182 }
183 
184 // This test verifies that one subnet with two pools can be properly
185 // translated from YANG to JSON using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV6,getPoolsKea)186 TEST_F(TranslatorSubnetsTestKeaV6, getPoolsKea) {
187     // Create the subnet 2001:db8::/48 #111.
188     const string& xpath = "/kea-dhcp6-server:config";
189     const string& xsub = xpath + "/subnet6[id='111']";
190     S_Val v_subnet(new Val("2001:db8::/48", SR_STRING_T));
191     const string& xsubnet = xsub + "/subnet";
192     EXPECT_NO_THROW(sess_->set_item(xsubnet.c_str(), v_subnet));
193 
194     // Create the pool 2001:db8::1:0/112.
195     const string& prefix1 = xsub + "/pool[start-address='2001:db8::1:0']" +
196         "[end-address='2001:db8::1:ffff']/prefix";
197     S_Val s_pool1(new Val("2001:db8::1:0/112", SR_STRING_T));
198     EXPECT_NO_THROW(sess_->set_item(prefix1.c_str(), s_pool1));
199 
200     // Create the pool 2001:db8::2:0/112.
201     const string& prefix2 = xsub + "/pool[start-address='2001:db8::2:0']" +
202         "[end-address='2001:db8::2:ffff']";
203     S_Val s_pool2;
204     EXPECT_NO_THROW(sess_->set_item(prefix2.c_str(), s_pool2));
205 
206     // Get the subnet.
207     ConstElementPtr subnet;
208     EXPECT_NO_THROW(subnet = t_obj_->getSubnet(xsub));
209     ASSERT_TRUE(subnet);
210     string expected =
211         "{\n"
212         "  \"id\": 111,\n"
213         "  \"pools\": [\n"
214         "    {\n"
215         "      \"pool\": \"2001:db8::1:0/112\"\n"
216         "    },\n"
217         "    {\n"
218         "      \"pool\": \"2001:db8::2:0 - 2001:db8::2:ffff\"\n"
219         "    }\n"
220         "  ],\n"
221         "  \"subnet\": \"2001:db8::/48\"\n"
222         "}";
223     EXPECT_EQ(expected, prettyPrint(subnet));
224 
225     // Get the subnet list and check if the subnet is in it.
226     ConstElementPtr subnets;
227     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
228     ASSERT_TRUE(subnets);
229     ASSERT_EQ(Element::list, subnets->getType());
230     ASSERT_EQ(1, subnets->size());
231     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
232 }
233 
234 // This test verifies that an empty subnet list can be properly
235 // translated from JSON to YANG using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,setEmptyIetf)236 TEST_F(TranslatorSubnetsTestIetfV6, setEmptyIetf) {
237     // Set empty list.
238     const string& xpath =
239         "/ietf-dhcpv6-server:server/server-config/network-ranges";
240     ConstElementPtr subnets = Element::createList();
241     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
242 
243     // Get it back.
244     subnets.reset();
245     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
246     ASSERT_FALSE(subnets);
247 }
248 
249 // This test verifies that an empty subnet list can be properly
250 // translated from JSON to YANG using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV4,setEmptyKea)251 TEST_F(TranslatorSubnetsTestKeaV4, setEmptyKea) {
252     // Set empty list.
253     const string& xpath = "/kea-dhcp4-server:config";
254     ElementPtr subnets = Element::createList();
255     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
256 
257     // Get it back.
258     subnets.reset();
259     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
260     ASSERT_FALSE(subnets);
261 }
262 
263 // This test verifies that one subnet can be properly
264 // translated from JSON to YANG using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,setIetf)265 TEST_F(TranslatorSubnetsTestIetfV6, setIetf) {
266     // Set one subnet.
267     const string& xpath =
268         "/ietf-dhcpv6-server:server/server-config/network-ranges";
269     ElementPtr subnets = Element::createList();
270     ElementPtr subnet = Element::createMap();
271     subnet->set("subnet", Element::create(string("2001:db8::/48")));
272     subnet->set("id", Element::create(123));
273     subnets->add(subnet);
274     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
275 
276     // Get it back.
277     subnets.reset();
278     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
279     ASSERT_TRUE(subnets);
280     ASSERT_EQ(Element::list, subnets->getType());
281     ASSERT_EQ(1, subnets->size());
282     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
283 }
284 
285 // This test verifies that one subnet can be properly
286 // translated from JSON to YANG using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV4,setKea)287 TEST_F(TranslatorSubnetsTestKeaV4, setKea) {
288     // Set one subnet.
289     const string& xpath = "/kea-dhcp4-server:config";
290     ElementPtr subnets = Element::createList();
291     ElementPtr subnet = Element::createMap();
292     subnet->set("subnet", Element::create(string("10.0.1.0/24")));
293     subnet->set("id", Element::create(123));
294     subnets->add(subnet);
295     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
296 
297     // Get it back.
298     subnets.reset();
299     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
300     ASSERT_TRUE(subnets);
301     ASSERT_EQ(Element::list, subnets->getType());
302     ASSERT_EQ(1, subnets->size());
303     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
304 
305     // Check it validates.
306     EXPECT_NO_THROW(sess_->validate());
307 }
308 
309 // This test verifies that one subnet with two pools can be properly
310 // translated from JSON to YANG using IETF model.
TEST_F(TranslatorSubnetsTestIetfV6,setTwoIetf)311 TEST_F(TranslatorSubnetsTestIetfV6, setTwoIetf) {
312     // Set one subnet.
313     const string& xpath =
314         "/ietf-dhcpv6-server:server/server-config/network-ranges";
315     ElementPtr subnets = Element::createList();
316     ElementPtr subnet = Element::createMap();
317     subnet->set("subnet", Element::create(string("2001:db8::/48")));
318     subnet->set("id", Element::create(123));
319 
320     // Add two pools.
321     ElementPtr pools = Element::createList();
322     ElementPtr pool1 = Element::createMap();
323     pool1->set("pool", Element::create(string("2001:db8::1:0/112")));
324     pools->add(pool1);
325     ElementPtr pool2 = Element::createMap();
326     pool2->set("pool", Element::create(string("2001:db8::2:0/112")));
327     pools->add(pool2);
328     subnet->set("pools", pools);
329 
330     // Add the subnet.
331     subnets->add(subnet);
332     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
333 
334     // Get it back.
335     subnets.reset();
336     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
337     ASSERT_TRUE(subnets);
338     ASSERT_EQ(Element::list, subnets->getType());
339     ASSERT_EQ(1, subnets->size());
340     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
341 }
342 
343 // This test verifies that one subnet with two pools can be properly
344 // translated from JSON to YANG using Kea ad hoc model.
TEST_F(TranslatorSubnetsTestKeaV4,setTwoKea)345 TEST_F(TranslatorSubnetsTestKeaV4, setTwoKea) {
346     // Set one subnet.
347     const string& xpath = "/kea-dhcp4-server:config";
348     ElementPtr subnets = Element::createList();
349     ElementPtr subnet = Element::createMap();
350     subnet->set("subnet", Element::create(string("10.0.1.0/24")));
351     subnet->set("id", Element::create(123));
352 
353     // Add two pools.
354     ElementPtr pools = Element::createList();
355     ElementPtr pool1 = Element::createMap();
356     pool1->set("pool", Element::create(string("10.0.1.0/28")));
357     pools->add(pool1);
358     ElementPtr pool2 = Element::createMap();
359     pool2->set("pool", Element::create(string("10.0.1.200 - 10.0.1.222")));
360     pools->add(pool2);
361     subnet->set("pools", pools);
362 
363     // Add the subnet.
364     subnets->add(subnet);
365     EXPECT_NO_THROW(t_obj_->setSubnets(xpath, subnets));
366 
367     // Get it back.
368     subnets.reset();
369     EXPECT_NO_THROW(subnets = t_obj_->getSubnets(xpath));
370     ASSERT_TRUE(subnets);
371     ASSERT_EQ(Element::list, subnets->getType());
372     ASSERT_EQ(1, subnets->size());
373     EXPECT_TRUE(subnet->equals(*subnets->get(0)));
374 
375     // Check it validates.
376     EXPECT_NO_THROW(sess_->validate());
377 }
378 
379 }  // namespace
380