1818package org .apache .cloudstack .dns .powerdns ;
1919
2020import java .io .IOException ;
21+ import java .util .List ;
2122
2223import org .apache .http .HttpStatus ;
2324import org .apache .http .client .config .RequestConfig ;
2425import org .apache .http .client .methods .CloseableHttpResponse ;
2526import org .apache .http .client .methods .HttpGet ;
27+ import org .apache .http .client .methods .HttpPost ;
28+ import org .apache .http .entity .StringEntity ;
2629import org .apache .http .impl .client .CloseableHttpClient ;
2730import org .apache .http .impl .client .HttpClientBuilder ;
2831import org .apache .http .util .EntityUtils ;
3235import com .cloud .utils .exception .CloudRuntimeException ;
3336import com .fasterxml .jackson .databind .JsonNode ;
3437import com .fasterxml .jackson .databind .ObjectMapper ;
38+ import com .fasterxml .jackson .databind .node .ArrayNode ;
39+ import com .fasterxml .jackson .databind .node .ObjectNode ;
3540
3641public class PowerDnsClient implements AutoCloseable {
3742 public static final Logger logger = LoggerFactory .getLogger (PowerDnsClient .class );
@@ -40,19 +45,7 @@ public class PowerDnsClient implements AutoCloseable {
4045 private final CloseableHttpClient httpClient ;
4146
4247 public void validate (String baseUrl , String apiKey ) {
43- String normalizedUrl = baseUrl .trim ();
44-
45- if (!normalizedUrl .startsWith ("http://" ) && !normalizedUrl .startsWith ("https://" )) {
46- normalizedUrl = "http://" + normalizedUrl ; // default to HTTP
47- }
48-
49- if (normalizedUrl .endsWith ("/" )) {
50- normalizedUrl = normalizedUrl .substring (0 , normalizedUrl .length () - 1 );
51- }
52-
53-
54- String checkUrl = normalizedUrl + "/api/v1/servers" ;
55-
48+ String checkUrl = buildApiUrl (baseUrl , "/api/v1/servers" );
5649 HttpGet request = new HttpGet (checkUrl );
5750 request .addHeader ("X-API-Key" , apiKey );
5851 request .addHeader ("Accept" , "application/json" );
@@ -94,6 +87,62 @@ public void validate(String baseUrl, String apiKey) {
9487 }
9588 }
9689
90+ public void createZone (String baseUrl , String apiKey , String zoneName , List <String > nameservers ) {
91+ String url = buildApiUrl (baseUrl , "/servers/localhost/zones" );
92+ ObjectNode json = MAPPER .createObjectNode ();
93+ json .put ("name" , zoneName .endsWith ("." ) ? zoneName : zoneName + "." );
94+ json .put ("kind" , "Native" );
95+ json .put ("dnssec" , false );
96+
97+ if (nameservers != null && !nameservers .isEmpty ()) {
98+ ArrayNode nsArray = json .putArray ("nameservers" );
99+ for (String ns : nameservers ) {
100+ nsArray .add (ns .endsWith ("." ) ? ns : ns + "." );
101+ }
102+ }
103+
104+ logger .debug ("Creating PowerDNS zone: {} using URL: {}" , zoneName , url );
105+
106+ HttpPost request = new HttpPost (url );
107+ request .addHeader ("X-API-Key" , apiKey );
108+ request .addHeader ("Content-Type" , "application/json" );
109+ request .addHeader ("Accept" , "application/json" );
110+
111+ try {
112+ request .setEntity (new StringEntity (json .toString ()));
113+
114+ try (CloseableHttpResponse response = httpClient .execute (request )) {
115+
116+ int statusCode = response .getStatusLine ().getStatusCode ();
117+ String body = response .getEntity () != null
118+ ? EntityUtils .toString (response .getEntity ())
119+ : null ;
120+
121+ if (statusCode == HttpStatus .SC_CREATED ) {
122+ logger .debug ("Zone {} created successfully" , zoneName );
123+ return ;
124+ }
125+
126+ if (statusCode == HttpStatus .SC_CONFLICT ) {
127+ throw new CloudRuntimeException ("Zone already exists: " + zoneName );
128+ }
129+
130+ if (statusCode == HttpStatus .SC_UNAUTHORIZED ||
131+ statusCode == HttpStatus .SC_FORBIDDEN ) {
132+ throw new CloudRuntimeException ("Invalid PowerDNS API key" );
133+ }
134+
135+ logger .debug ("Unexpected PowerDNS response: HTTP {} Body: {}" , statusCode , body );
136+
137+ throw new CloudRuntimeException (String .format ("Failed to create zone %s (HTTP %d)" , zoneName , statusCode ));
138+ }
139+
140+ } catch (IOException e ) {
141+ throw new CloudRuntimeException ("Error while creating PowerDNS zone " + zoneName , e );
142+ }
143+ }
144+
145+
97146 public PowerDnsClient () {
98147 RequestConfig config = RequestConfig .custom ()
99148 .setConnectTimeout (TIMEOUT_MS )
@@ -107,6 +156,24 @@ public PowerDnsClient() {
107156 .build ();
108157 }
109158
159+ private String normalizeBaseUrl (String baseUrl ) {
160+ if (baseUrl == null ) {
161+ throw new IllegalArgumentException ("PowerDNS base URL cannot be null" );
162+ }
163+ String normalizedUrl = baseUrl .trim ();
164+ if (!normalizedUrl .startsWith ("http://" ) && !normalizedUrl .startsWith ("https://" )) {
165+ normalizedUrl = "http://" + normalizedUrl ;
166+ }
167+ if (normalizedUrl .endsWith ("/" )) {
168+ normalizedUrl = normalizedUrl .substring (0 , normalizedUrl .length () - 1 );
169+ }
170+ return normalizedUrl ;
171+ }
172+
173+ private String buildApiUrl (String baseUrl , String path ) {
174+ return normalizeBaseUrl (baseUrl ) + "/api/v1" + path ;
175+ }
176+
110177 @ Override
111178 public void close () {
112179 try {
0 commit comments