Coverage for birdplan/plugins/cmdline/configure.py: 93%

60 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-23 03:27 +0000

1# 

2# SPDX-License-Identifier: GPL-3.0-or-later 

3# 

4# Copyright (c) 2019-2024, AllWorldIT 

5# 

6# This program is free software: you can redistribute it and/or modify 

7# it under the terms of the GNU General Public License as published by 

8# the Free Software Foundation, either version 3 of the License, or 

9# (at your option) any later version. 

10# 

11# This program is distributed in the hope that it will be useful, 

12# but WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14# GNU General Public License for more details. 

15# 

16# You should have received a copy of the GNU General Public License 

17# along with this program. If not, see <http://www.gnu.org/licenses/>. 

18 

19"""BirdPlan commandline options for "birdplan configure".""" 

20 

21 

22import argparse 

23import grp 

24import os 

25import pwd 

26from typing import Any, Dict, Optional 

27 

28from ...cmdline import BIRD_CONFIG_FILE, BirdPlanCommandLine, BirdPlanCommandlineResult 

29from ...exceptions import BirdPlanError 

30from .cmdline_plugin import BirdPlanCmdlinePluginBase 

31 

32__all__ = ["BirdPlanCmdlineConfigure"] 

33 

34 

35class BirdPlanCmdlineConfigure(BirdPlanCmdlinePluginBase): 

36 """BirdPlan "configure" command.""" 

37 

38 # Output config file 

39 _config_filename: Optional[str] 

40 

41 def __init__(self) -> None: 

42 """Initialize object.""" 

43 

44 super().__init__() 

45 

46 # Plugin setup 

47 self.plugin_description = "birdplan configure" 

48 self.plugin_order = 10 

49 

50 self._config_filename = None 

51 

52 def register_parsers(self, args: Dict[str, Any]) -> None: 

53 """ 

54 Register commandline parsers. 

55 

56 Parameters 

57 ---------- 

58 args : Dict[str, Any] 

59 Method argument(s). 

60 

61 """ 

62 

63 root_parser = args["root_parser"] 

64 

65 # CMD: configure 

66 subparser = root_parser.add_parser("configure", help="Create BIRD configuration") 

67 

68 subparser.add_argument( 

69 "--action", 

70 action="store_const", 

71 const="configure", 

72 default="configure", 

73 help=argparse.SUPPRESS, 

74 ) 

75 

76 # Output filename 

77 subparser.add_argument( 

78 "-o", 

79 "--output-file", 

80 nargs=1, 

81 metavar="BIRD_CONFIG_FILE", 

82 default=[BIRD_CONFIG_FILE], 

83 help=f"BIRD config file to output, using '-' will output to stdout (default: {BIRD_CONFIG_FILE})", 

84 ) 

85 

86 # Ignore IRR changes 

87 subparser.add_argument( 

88 "--ignore-irr-changes", action="store_true", default=False, help="Ignore IRR changes between last run and this run" 

89 ) 

90 

91 # Ignore PeeringDB changes 

92 subparser.add_argument( 

93 "--ignore-peeringdb-changes", 

94 action="store_true", 

95 default=False, 

96 help="Ignore PeeringDB changes between last run and this run", 

97 ) 

98 

99 # Use last cached data 

100 subparser.add_argument( 

101 "--use-cached", 

102 action="store_true", 

103 default=False, 

104 help="Use cached IRR and PeeringDB data instead of doing network requests", 

105 ) 

106 

107 # Set our internal subparser property 

108 self._subparser = subparser 

109 self._subparsers = None 

110 

111 def cmd_configure(self, args: Any) -> Any: 

112 """ 

113 Commandline handler for "configure" action. 

114 

115 Parameters 

116 ---------- 

117 args : Dict[str, Any] 

118 Method argument(s). 

119 

120 """ 

121 

122 cmdline: BirdPlanCommandLine = args["cmdline"] 

123 

124 # Load BirdPlan configuration 

125 cmdline.birdplan_load_config( 

126 ignore_irr_changes=cmdline.args.ignore_irr_changes, 

127 ignore_peeringdb_changes=cmdline.args.ignore_peeringdb_changes, 

128 use_cached=cmdline.args.use_cached, 

129 ) 

130 # Generate BIRD configuration 

131 bird_config = cmdline.birdplan.configure() 

132 

133 # Commit BirdPlan state 

134 cmdline.birdplan_commit_state() 

135 

136 # Save the output filename 

137 self.config_filename = cmdline.args.output_file[0] 

138 

139 # If we're outputting to file, write it here 

140 if self.config_filename and self.config_filename != "-": 

141 self._write_config_file(bird_config) 

142 return BirdPlanCommandlineResult(bird_config, has_console_output=False) 

143 

144 return BirdPlanCommandlineResult(bird_config) 

145 

146 def _write_config_file(self, data: Any) -> None: 

147 """ 

148 Write out configuration file with data. 

149 

150 Parameters 

151 ---------- 

152 data : str 

153 Bird configuration 

154 

155 """ 

156 

157 if not self.config_filename: 

158 raise RuntimeError("Attribute 'config_filename' must be set") 

159 

160 # Get birdplan user id 

161 try: 

162 birdplan_uid = pwd.getpwnam("birdplan").pw_uid 

163 except KeyError: 

164 birdplan_uid = -1 

165 

166 # Get bird group id 

167 try: 

168 bird_gid = grp.getgrnam("bird").gr_gid 

169 except KeyError: 

170 bird_gid = None 

171 

172 # Write out config file 

173 try: 

174 fd = os.open(self.config_filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o640) 

175 # If we have a bird group, set it 

176 if bird_gid: 

177 os.fchown(fd, birdplan_uid, bird_gid) 

178 # Write out config 

179 with os.fdopen(fd, "w") as config_file: 

180 config_file.write(data) 

181 except OSError as err: # pragma: no cover 

182 raise BirdPlanError(f"Failed to open '{self.config_filename}' for writing: {err}") from None 

183 

184 @property 

185 def config_filename(self) -> Optional[str]: 

186 """Config file name to write out.""" 

187 return self._config_filename 

188 

189 @config_filename.setter 

190 def config_filename(self, config_filename: str) -> None: 

191 """Config file name to write out.""" 

192 self._config_filename = config_filename