mcp-souschef 3.0.0__py3-none-any.whl → 3.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ """
2
+ Chef Server Settings Page for SousChef UI.
3
+
4
+ Configure and validate Chef Server connectivity for dynamic inventory and node queries.
5
+ """
6
+
7
+ import os
8
+
9
+ import streamlit as st
10
+
11
+ from souschef.core.url_validation import validate_user_provided_url
12
+
13
+ try:
14
+ import requests
15
+ from requests.exceptions import (
16
+ ConnectionError, # noqa: A004
17
+ Timeout,
18
+ )
19
+ except ImportError:
20
+ requests = None # type: ignore[assignment]
21
+ ConnectionError = Exception # type: ignore[assignment,misc] # noqa: A001
22
+ Timeout = Exception # type: ignore[assignment,misc]
23
+
24
+
25
+ def _handle_chef_server_response(
26
+ response: "requests.Response", server_url: str
27
+ ) -> tuple[bool, str]:
28
+ """
29
+ Handle Chef Server search response.
30
+
31
+ Args:
32
+ response: HTTP response from Chef Server
33
+ server_url: The Chef Server URL that was queried
34
+
35
+ Returns:
36
+ Tuple of (success: bool, message: str)
37
+
38
+ """
39
+ if response.status_code == 200:
40
+ return True, f"✅ Successfully connected to Chef Server at {server_url}"
41
+ if response.status_code == 401:
42
+ return (
43
+ False,
44
+ "❌ Authentication failed - check your Chef Server credentials",
45
+ )
46
+ if response.status_code == 404:
47
+ return False, "❌ Chef Server search endpoint not found"
48
+ return (
49
+ False,
50
+ f"❌ Connection failed with status code {response.status_code}",
51
+ )
52
+
53
+
54
+ def _validate_chef_server_connection(
55
+ server_url: str, node_name: str
56
+ ) -> tuple[bool, str]:
57
+ """
58
+ Validate Chef Server connection by testing the search endpoint.
59
+
60
+ Args:
61
+ server_url: Base URL of the Chef Server
62
+ node_name: Chef node name for authentication
63
+
64
+ Returns:
65
+ Tuple of (success: bool, message: str)
66
+
67
+ """
68
+ if not requests:
69
+ return False, "requests library not installed"
70
+
71
+ if not server_url:
72
+ return False, "Server URL is required"
73
+
74
+ try:
75
+ server_url = validate_user_provided_url(server_url)
76
+ except ValueError as exc:
77
+ return False, f"Invalid server URL: {exc}"
78
+
79
+ if not node_name:
80
+ return False, "Node name is required for authentication"
81
+
82
+ # Test the search endpoint
83
+ try:
84
+ search_url = f"{server_url.rstrip('/')}/search/node"
85
+ response = requests.get(
86
+ search_url,
87
+ params={"q": "*:*"},
88
+ timeout=5,
89
+ headers={"Accept": "application/json"},
90
+ )
91
+ return _handle_chef_server_response(response, server_url)
92
+
93
+ except Timeout:
94
+ return False, f"❌ Connection timeout - could not reach {server_url}"
95
+ except ConnectionError:
96
+ return False, f"❌ Connection error - Chef Server not reachable at {server_url}"
97
+ except Exception as e:
98
+ return False, f"❌ Unexpected error: {e}"
99
+
100
+
101
+ def _render_chef_server_configuration() -> tuple[str, str]:
102
+ """
103
+ Render Chef Server configuration UI and return config values.
104
+
105
+ Returns:
106
+ Tuple of (server_url, node_name)
107
+
108
+ """
109
+ st.subheader("Chef Server Configuration")
110
+
111
+ st.markdown("""
112
+ Configure your Chef Server connection for dynamic inventory generation
113
+ and node queries. This allows SousChef to retrieve live node data from
114
+ your Chef infrastructure.
115
+ """)
116
+
117
+ col1, col2 = st.columns(2)
118
+
119
+ with col1:
120
+ server_url = st.text_input(
121
+ "Chef Server URL",
122
+ help="Full URL of your Chef Server (e.g., https://chef.example.com)",
123
+ key="chef_server_url_input",
124
+ placeholder="https://chef.example.com",
125
+ value=os.environ.get("CHEF_SERVER_URL", ""),
126
+ )
127
+
128
+ with col2:
129
+ node_name = st.text_input(
130
+ "Chef Node Name",
131
+ help="Node name for authentication with Chef Server",
132
+ key="chef_node_name_input",
133
+ placeholder="my-node",
134
+ value=os.environ.get("CHEF_NODE_NAME", ""),
135
+ )
136
+
137
+ return server_url, node_name
138
+
139
+
140
+ def _render_test_connection_button(server_url: str, node_name: str) -> None:
141
+ """
142
+ Render the test connection button and display results.
143
+
144
+ Args:
145
+ server_url: Chef Server URL to test
146
+ node_name: Chef node name for authentication
147
+
148
+ """
149
+ st.markdown("---")
150
+ st.subheader("Test Connection")
151
+
152
+ col1, col2 = st.columns([1, 3])
153
+
154
+ with col1:
155
+ test_button = st.button(
156
+ "Test Chef Server Connection",
157
+ type="primary",
158
+ help="Verify connectivity to Chef Server",
159
+ )
160
+
161
+ if test_button:
162
+ with col2, st.spinner("Testing Chef Server connection..."):
163
+ success, message = _validate_chef_server_connection(server_url, node_name)
164
+
165
+ if success:
166
+ st.success(message)
167
+ else:
168
+ st.error(message)
169
+
170
+
171
+ def _render_usage_examples() -> None:
172
+ """Render usage examples for Chef Server integration."""
173
+ st.markdown("---")
174
+ st.subheader("Usage Examples")
175
+
176
+ with st.expander("Dynamic Inventory from Chef Searches"):
177
+ st.markdown("""
178
+ Once configured, SousChef can query your Chef Server to generate dynamic
179
+ Ansible inventories based on Chef node searches:
180
+
181
+ ```python
182
+ # Example Chef search query
183
+ search_query = "role:webserver AND chef_environment:production"
184
+
185
+ # SousChef will convert this to an Ansible dynamic inventory
186
+ # that queries your Chef Server in real-time
187
+ ```
188
+
189
+ Benefits:
190
+ - Real-time node discovery
191
+ - No manual inventory maintenance
192
+ - Leverage existing Chef infrastructure
193
+ - Seamless migration path
194
+ """)
195
+
196
+ with st.expander("Environment Variables"):
197
+ st.markdown("""
198
+ You can also configure Chef Server settings via environment variables:
199
+
200
+ ```bash
201
+ export CHEF_SERVER_URL="https://chef.example.com"
202
+ export CHEF_NODE_NAME="my-node"
203
+ ```
204
+
205
+ These will be automatically detected by SousChef.
206
+ """)
207
+
208
+
209
+ def _render_save_settings_section(server_url: str, node_name: str) -> None:
210
+ """
211
+ Render the save settings section.
212
+
213
+ Args:
214
+ server_url: Chef Server URL to save
215
+ node_name: Chef node name to save
216
+
217
+ """
218
+ st.markdown("---")
219
+ st.subheader("Save Settings")
220
+
221
+ col1, col2 = st.columns([1, 3])
222
+
223
+ with col1:
224
+ save_button = st.button(
225
+ "Save Configuration",
226
+ type="primary",
227
+ help="Save Chef Server settings to session",
228
+ )
229
+
230
+ if save_button:
231
+ with col2:
232
+ # Save to session state
233
+ st.session_state.chef_server_url = server_url
234
+ st.session_state.chef_node_name = node_name
235
+
236
+ st.success("""
237
+ ✅ Chef Server configuration saved to session!
238
+
239
+ **Note:** For persistent configuration across sessions,
240
+ set environment variables:
241
+ - `CHEF_SERVER_URL`
242
+ - `CHEF_NODE_NAME`
243
+ """)
244
+
245
+
246
+ def _render_current_configuration() -> None:
247
+ """Display current Chef Server configuration from environment or session."""
248
+ current_url = os.environ.get("CHEF_SERVER_URL") or st.session_state.get(
249
+ "chef_server_url", "Not configured"
250
+ )
251
+ current_node = os.environ.get("CHEF_NODE_NAME") or st.session_state.get(
252
+ "chef_node_name", "Not configured"
253
+ )
254
+
255
+ st.info(f"""
256
+ **Current Configuration:**
257
+ - Server URL: `{current_url}`
258
+ - Node Name: `{current_node}`
259
+ """)
260
+
261
+
262
+ def show_chef_server_settings_page() -> None:
263
+ """Display Chef Server settings and configuration page."""
264
+ st.title("🔧 Chef Server Settings")
265
+
266
+ st.markdown("""
267
+ Configure your Chef Server connection to enable dynamic inventory generation
268
+ and live node queries. This allows SousChef to integrate with your existing
269
+ Chef infrastructure during the migration process.
270
+ """)
271
+
272
+ # Display current configuration
273
+ _render_current_configuration()
274
+
275
+ st.markdown("---")
276
+
277
+ # Configuration inputs
278
+ server_url, node_name = _render_chef_server_configuration()
279
+
280
+ # Test connection
281
+ _render_test_connection_button(server_url, node_name)
282
+
283
+ # Save settings
284
+ _render_save_settings_section(server_url, node_name)
285
+
286
+ # Usage examples
287
+ _render_usage_examples()
288
+
289
+ # Additional information
290
+ st.markdown("---")
291
+ st.markdown("""
292
+ ### Security Note
293
+
294
+ Chef Server authentication typically requires:
295
+ - Client key file for API authentication
296
+ - Proper permissions on the Chef Server
297
+
298
+ For production use, ensure your Chef Server credentials are properly secured
299
+ and not committed to version control.
300
+ """)