diff options
Diffstat (limited to 'ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools')
2 files changed, 1104 insertions, 0 deletions
diff --git a/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/Generator.java b/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/Generator.java new file mode 100644 index 0000000..397b827 --- /dev/null +++ b/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/Generator.java @@ -0,0 +1,76 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.openstack.controller.tools;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+
+import org.openecomp.utils.YamlToJava;
+import org.openecomp.ncomp.openstack.OpenstackFactory;
+import org.openecomp.ncomp.openstack.location.LocationFactory;
+import org.openecomp.ncomp.sirius.manager.controllermodel.ControllerModel;
+import org.openecomp.ncomp.sirius.manager.controllermodel.ControllermodelFactory;
+import org.openecomp.ncomp.sirius.manager.generator.ControllerGenerator;
+//import org.openecomp.ncomp.sirius.manager.gui.GuiFactory;
+import org.openecomp.ncomp.sirius.manager.server.ServerPackage;
+
+
+public class Generator {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ @SuppressWarnings("unused")
+ ServerPackage f = ServerPackage.eINSTANCE;
+ EObject o = OpenstackFactory.eINSTANCE.createOpenStackController();
+ EPackage p = o.eClass().getEPackage();
+ String dir = p.getNsURI().replace(p.getNsPrefix(),"") + "servers." + p.getNsPrefix();
+ dir= "src/main/sirius-gen/" + dir.replace('.', '/');
+ ControllerModel m = ControllermodelFactory.eINSTANCE.createControllerModel();
+ m.setTemplateDirectory("../../dcae-org.openecomp.ncomp.sirius.manager/ncomp-sirius-manager-generator/src/main/templates");
+ m.setPrefix("Os");
+ m.setPluginName(p.getNsURI());
+ m.setName("OpenStackController");
+ m.setTitle("OpenStack Controller");
+ ControllerGenerator g = new ControllerGenerator(o,m);
+ EObject loc =LocationFactory.eINSTANCE.createOpenStackLocation();
+ g.addObject("loc",loc,m);
+ g.setEnableIRequestHandler(false);
+ //EObject gui = GuiFactory.eINSTANCE.createGuiClientApi();
+ //g.addObject("gui",gui,m);
+ //EObject e = OpenstackFactory.eINSTANCE.createVpnEnterprise();
+ //g.addApi("e",e,m);
+ //g.addFactory("org.openecomp.ncomp.sirius.servers.openstack.OsOpenstackFactory");
+ g.generate(dir);
+ g.generateAnt("./ant.xml",true,true);
+ g.generateScripts("src/main/server-gen/bin","openstack-controller");
+ String pName = p.getNsURI().replaceAll(p.getNsPrefix()+'$',"") + "servers." + p.getNsPrefix() +".logging";
+ YamlToJava.convert("src/main/resources/OpenStackAdaptor.yaml", dir + "/logging", pName);
+ YamlToJava.convert("src/main/sirius-gen/OpenStackController.yaml", dir + "/logging", pName);
+ String pName1 = p.getNsURI().replaceAll(p.getNsPrefix()+'$',"") + "servers." + p.getNsPrefix() +".loc.logging";
+ YamlToJava.convert("src/main/sirius-gen/OpenStackLocation.yaml", dir + "/loc/logging", pName1);
+
+ }
+
+
+}
diff --git a/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/OpenStackUtil.groovy b/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/OpenStackUtil.groovy new file mode 100644 index 0000000..55155fc --- /dev/null +++ b/ncomp-openstack-controller/src/main/java/org/openecomp/ncomp/openstack/controller/tools/OpenStackUtil.groovy @@ -0,0 +1,1028 @@ + +/*- + * ============LICENSE_START========================================== + * OPENECOMP - DCAE + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + */ + +package org.openecomp.ncomp.openstack.controller.tools + +import org.openecomp.ncomp.sirius.manager.JavaHttpClient; +import org.openecomp.ncomp.sirius.manager.Jetty8Client; +import org.openecomp.ncomp.sirius.manager.ManagementServer; +import org.openecomp.ncomp.sirius.manager.Subject; +import org.openecomp.ncomp.sirius.manager.server.Response +import org.openecomp.ncomp.core.NamedEntity +import org.openecomp.ncomp.core.User +import org.openecomp.ncomp.openstack.* +import org.openecomp.ncomp.openstack.core.* +import org.openecomp.ncomp.openstack.location.* +import org.openecomp.ncomp.openstack.compute.* +import org.openecomp.ncomp.openstack.location.OpenStackLocation +import org.openecomp.ncomp.openstack.location.OpenStackProject +import org.openecomp.ncomp.openstack.neutron.CreateNetworkRequest; +import org.openecomp.ncomp.openstack.neutron.NeutronFactory; +import org.openecomp.ncomp.openstack.neutron.NeutronObject; +import org.openecomp.ncomp.openstack.neutron.NeutronPackage; +import org.openecomp.ncomp.openstack.neutron.NeutronRequest +import org.openecomp.ncomp.webservice.utils.FileUtils; + +import org.apache.log4j.Logger; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EAttribute +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage +import org.eclipse.emf.ecore.EReference +import org.eclipse.emf.ecore.util.EcoreUtil +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpExchange; +import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.BufferUtil; +import org.eclipse.jetty.io.ByteArrayBuffer +import org.json.JSONObject +import org.json.JSONArray + +import static org.openecomp.ncomp.utils.PropertyUtil.getPropertiesFromClasspath + + +class OpenStackUtil { + public static final Logger logger = Logger.getLogger(OpenStackUtil.class); + public static final Logger plogger = Logger.getLogger("org.openecomp.ncomp.openstack.OpenStackUtil.polling"); + Properties props + List<OpenStackControllerProject> projects = [] + ManagementServer server + OpenStackLocation loc // used for polling + OpenStackLocation realLocation // location in controller state. + boolean debug = false + int serverCreationTimeout + public long pollingFrequency = 300000 + + public OpenStackUtil(OpenStackLocation loc1, ManagementServer server1, String file = "openstack.properties") { + props = getPropertiesFromClasspath(file); + EPackage p1 = LocationPackage.eINSTANCE; + debug = Boolean.parseBoolean(props.getProperty("server.debug","false")); + serverCreationTimeout = Integer.parseInt(props.get("serverCreationTimeout","120")) + pollingFrequency = Integer.parseInt(props.get("pollingFrequency","300000")) + server = server1 + realLocation = loc1 + loc = EcoreUtil.copy(loc1) + loc.projects.each { p -> + projects += new OpenStackControllerProject(p) + } + System.err.println "OPENSTACK: debug=$debug pollingFrequency=$pollingFrequency" + logger.info("Found ${projects.size()} projects".toString()) + if (!projects.find { it.tenantName == "admin"}) { + logger.warn("Missing Admin Openstack project") + } + } + static main(args) { + // BROKEN WITH THE NEW SETUP. Need to create Location object + // ALWAYS USE GMT. + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + OpenStackUtil openstack = new OpenStackUtil() + switch ("T") { + case "T": + CreateNetworkRequest r = NeutronFactory.eINSTANCE.createCreateNetworkRequest() + r.name = "someone-test" + r.tenant_id = openstack.projects[0].tenantId + openstack.projects[0].create(r) + break + case "P": + openstack.poll() + println openstack.server.ecore2json(openstack.loc, 100, null, false).toString(2) + break + case "S": openstack.projects[0].createServer(); break + } + + } + public OpenStackLocation poll() { + if (loc.keystoneUrl == null) { + logger.warn("No keystoneUrl for $loc.name") + return + } + plogger.info "Starting Logging" + if (props.get("noPoll","no") == "yes") return null + loc.images.clear() + def oldFlavors = [] + loc.flavors.each { f -> oldFlavors += f } + loc.flavors.clear() + + if (projects.size() == 0) { + logger.warn("No project to poll") + return + } + + projects[0].updateGeneric(loc,"image") + try { + projects[0].updateGeneric(loc,"flavor") + } + catch (e) { + } + if (loc.flavors.size() == 0) { + logger.warn("Empty Flavor list, using old") + System.err.println("Empty Flavor list, using old") + oldFlavors.each { f -> loc.flavors.add(f) } + } + projects.each {OpenStackControllerProject p -> + p.poll() + } + realLocation.mergeLocation(loc) + server.save() +// plogger.debug("${server.ecore2json(loc, 100, null, false).toString(2)}".toString()) + plogger.info "Done Logging" + + return loc + } + static def object2json(o,boolean fix = false) { + switch (o) { + case JSONObject: return o + case EObject: return ManagementServer.ecore2json(o, 100, null, false) + case List: + def j = [] as JSONArray + o.each { j.put(object2json(it,fix)) } + return j + case Map: + def j = [:] as JSONObject + o.each { String k,v -> + if (v == null) return + if (!fix) {j.put(k,object2json(v)); return} + switch (k) { + case "addresses": + def a = [] as JSONArray + j.put(k,a) + v.each { k1, v1 -> + a.put(object2json([ name : k1, ips : v1],fix)) + } + break + case "metadata": + def a = [] as JSONArray + j.put(k,a) + v.each { k1, v1 -> + a.put(object2json([ key : k1, value :v1],fix)) + } + break + case ~/.*:.*/: + // eg OS-EXT-STS:vm_state + j.put(k.replace("-", "_").replace(":", "_"),object2json(v,fix)) +// String[] a = k.split(":") +// String nn = a[1] +// def j1 = [:] as JSONObject +// j1.put(nn,object2json(v,fix)) +// j.put(a[0].replace("-", "_").toLowerCase(),j1) + break + default: j.put(k,object2json(v,fix)) + } + } + return j + case boolean: case Boolean: + case int: case Integer: + case long: case Long: + case double: case Double: + case JSONObject.Null: + case null: + case String: return o; break + case GString: return o.toString(); break + default: logger.warn "Unable to convert: $o".toString() + } + } + def json2object (o) { + switch (o) { + case JSONObject: + def m = [:] + o.map.each { n,v -> m[n] = json2object(v) } + return m + case JSONArray: + def a = [] + o.myArrayList.each { a += json2object(it) } + return a + case boolean: case Boolean: + case int: case Integer: + case long: case Long: + case double: case Double: + case null: + case JSONObject.NULL: + case String: return o; break + case GString: return o.toString(); break + default: logger.warn "Unable to convert to object: $o ${o.getClass().name}".toString() + } + } + //{"flavor": {"vcpus": 1, "disk": 10, "name": "someone.test", "os-flavor-access:is_public": true, "rxtx_factor": 1, "OS-FLV-EXT-DATA:ephemeral": 33, "ram": 200, "id": 10001, "swap": 0}} + def x = [:] + def createFlavor(String projectName, VirtualMachineType vmType) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == projectName} + if (p == null) return "No project with name $projectName" + def fName = vmType.name + def i = null + switch (loc.version) { + case OpenStackVersion.FOLSOM: // old BSA version + def flavors = p.httpJsonTransaction([url : "${p.urls['nova']}/flavors"],false) + x.flavors = flavors + def existingId = null + synchronized (loc) { + i = loc.flavorId + 1 + flavors.flavors.each { ff -> + def j = Integer.parseInt(ff.id) + // if (debug) System.err.println "Existing Flavors: id=$ff.id name=$ff.name" + if (i <= j) i = j + 1 + if (fName == ff.name) existingId = ff.id + } + loc.flavorId = i + server.save() + } + break; + } + def req = [ + url : "${p.urls['nova']}/flavors", + method : "POST", + body : [ flavor : [ + vcpus: vmType.numberOfCores, + disk: vmType.rootDiskSizeGB, + name: fName, + "os-flavor-access:is_public": true, + rxtx_factor: 1, + "OS-FLV-EXT-DATA:ephemeral": vmType.diskSizeGB, + ram: vmType.memorySizeMB, + swap: 0] + ] + ] + if (i != null) req.body.flavor.identity = i + x.req = req + def res = p.httpJsonTransaction(req,false) + x.res = res + } + def createSecurityGroup(String projectName, VirtualMachineType vmType) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == projectName} + if (p == null) return "No project with name $projectName" + def gName = vmType.name + def req = [ + url : "${p.urls['nova']}/os-security-groups", + method : "POST", + body : [ security_group : [ name: gName, description : "Automatically created" ] ] + ] + x.req = req + def res = p.httpJsonTransaction(req,false) + // {"security_group_rule": {"from_port": 22, "ip_protocol": "tcp", "to_port": 22, "parent_group_id": 12, "cidr": "0.0.0.0/0", "group_id": null}} + vmType.incomingSecurityRules.each { SecurityRule r -> + def id = res.security_group.id + def req2 = [ + url : "${p.urls['nova']}/os-security-group-rules", + method : "POST", + body : [ security_group_rule : [ + from_port: r.portRangeStart, + to_port: r.portRangeEnd, + ip_protocol: r.ipProtocol.toString(), + cidr: r.prefix, + parent_group_id: id, + ] ] + ] + def res2 = p.httpJsonTransaction(req2,false) + } + x.res = res + } + def delete(String projectName, String type, String name) { + if (debug) System.err.println "DELETING $projectName $type $name" + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == projectName} + if (p == null) return "No project with name $projectName" + def types = "${type}s" + def idName = "id" + def oName = "name" + def api = "nova" + def dtypes = types + def nameAttr = loc.version == OpenStackVersion.FOLSOM ? "display_name" : "name" + switch (type) { + case "volume": oName = nameAttr; api = "cinder"; break + case "flavor": types = "$types/detail"; break + case "security_group": types = "os-security-groups"; dtypes = types; break + case "keypair": + p.httpJsonTransaction([url : "${p.urls[api]}/os-$types/$name", method:"DELETE"],false,true,true,false) + if (debug) System.err.println "$type name=$name deleted" + return + } + def l = p.httpJsonTransaction([url : "${p.urls[api]}/$types"],false) + if (debug) System.err.println "l=$l" + if (debug) System.err.println "DELETE LIST:" + l["${type}s"].each { if (debug) System.err.println it } + if (debug) System.err.println "type=$type types=$types oName=$oName name=$name idName=$idName" + def fl = l["${type}s"].findAll { it[oName] == name } + if (debug) System.err.println "DELETEING LIST: $fl" + if (fl.size() == null) return "No $type with name $name" + fl.each { f -> + p.httpJsonTransaction([url : "${p.urls[api]}/$dtypes/${f[idName]}", method:"DELETE"],false,true,true,false) + if (debug) System.err.println "$type name=$name id=${f[idName]} deleted" + } + } + + // {"volume": {"status": "creating", "availability_zone": "nova:lab2-bsa-02-bay04", + // "display_description": null, "snapshot_id": null, "user_id": null, "size": 2, + // "display_name": "someone.test", "imageRef": null, "attach_status": "detached", + // "volume_type": null, "project_id": null, "metadata": {}}} + def createVolume (OpenstackRequestNewServer req) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == req.projectName} + if (p == null) return "No project with name $projectName" + def reqV = [url : "${p.urls['cinder']}/volumes/detail"] + def resV = p.httpJsonTransaction(reqV,false) +// System.out.println("VVVVVVVVVVVVVVVVVV: ${reqV}") +// System.out.println("VVVVVVVVVVVVVVVVVV: ${resV}") + def nameAttr = loc.version == OpenStackVersion.FOLSOM ? "display_name" : "name" + def existingVol = resV.volumes.find { vol -> + ( vol["display_name"] ?: vol["name"] ) == req.name + } + if (existingVol != null) { + if (debug) System.err.println "EXISTING VOLUME: ${existingVol}" + switch (loc.version) { + case OpenStackVersion.FOLSOM: + if (existingVol.availability_zone != "nova:$req.hypervisor") + return [status:"error",message:"existing volume on wrong hypervisor: $existingVol.availability_zone"] + } + if ("$existingVol.size" != "$req.vmType.volumeSizeGB") + return [status:"error",message:"existing volume has wrong size: $existingVol.size"] + if (existingVol.status != "available") + return [status:"error",message:"existing volume is not available: $existingVol.status"] + return [status: "ok", volume: existingVol] + } + def req1 = [ + url : "${p.urls['cinder']}/volumes", + method : "POST", + body : [ volume : [ + display_name: req.name, + size: req.vmType.volumeSizeGB] + ] + ] + if (req.hypervisor != null) { + req1.body.volume.availability_zone = "nova:$req.hypervisor" + } + def res1 = p.httpJsonTransaction(req1,false,false) +// System.out.println("VVVVVVVVVVVVVVVVVV req1: ${req1}") +// System.out.println("VVVVVVVVVVVVVVVVVV res1: ${res1}") + def volume = res1.volume ?: null + // wait for the new volume to be available. Wait at most 10 seconds. + (1..10).each { + if (volume == null || volume.status == "available") return + sleep 1000 + res1 = p.httpJsonTransaction([url : "${p.urls['cinder']}/volumes/$volume.id"],false,false) +// System.out.println("VVVVVVVVVVVVVVVVVV res1 LOOP: ${res1}") + volume = res1.volume ?: null + } + if (volume == null) return [status:"error",message:res1] + if (volume.status != "available") return [status:"error",message:"new volume did not become available"] + return [status: "ok", volume: volume] + } + // {"server": {"name": "someone.test", "imageRef": "9a460998-6501-4008-9eed-a510cf9a13bc", + // "availability_zone": "nova:lab3-bsa-03-bay03", "key_name": "someone", "flavorRef": "2", + // "max_count": 1, "min_count": 1, "networks": [], + // "security_groups": [{"name": "default"}, {"name": "ssh"}]}} + def createServer (OpenstackRequestNewServer req) { + OpenStackControllerProject p + def loc = realLocation + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == req.projectName} + if (p == null) throw new RuntimeException( "No project with name $req.projectName" ) + if (req.vmType == null) throw new RuntimeException("Null VmType") + String flavorName = req.vmType.flavorName ?: req.vmType.name + def flavor = loc.flavors.find { it.name == flavorName } + if (flavor == null) { + System.err.println "flavors $loc.name: ${loc.flavors.collect {it.name}}" + throw new RuntimeException("No flavor with name '$flavorName'") + } + if (req.vmType.imageName == null) req.vmType.imageName = req.vmType.name + def image = loc.images.find { it.name == req.vmType.imageName } + if (image == null) throw new RuntimeException( "No image with name $req.vmType.imageName") + def keypair = p.project.keypairs.find { it.name == req.user } + if (req.user != null && keypair == null) throw new RuntimeException( "No key pair with name $req.user") + def req1 = [ + url : "${p.urls['nova']}/servers", + method : "POST", + body : [ server : [ + name: req.name, + imageRef: image.id, + key_name: req.user, + flavorRef: flavor.id, + max_count: 1, min_count: 1, networks: [], + security_groups: [[name: "default"]], + config_drive: true, + ], + ] + ] + if (! loc.supportsSecurityGroups) req1.body.server.remove('security_groups') + req.networks.each {network -> req1.body.server.networks += [uuid:network]} + req.ports.each {port -> req1.body.server.networks += [port:port]} + if (req.hypervisor != null) { + req1.body.server.availability_zone = "nova:$req.hypervisor" + } + if (req.user_data != null) { + req1.body.server.user_data = req.user_data + } + x["req"] = req1 + logger.info "create server request: ${object2json(req1).toString(2)}" + def server = p.httpJsonTransaction(req1,false) + logger.info "create server return: ${object2json(server).toString(2)}" + if (! server.server) { + throw new RuntimeException("create server failed: ${object2json(server).toString(2)}") + } + def vol + if (req.vmType.volumeSizeGB > 0) { + vol = createVolume(req) +// System.err.println "VOLUME= $req $vol " + if (vol.status != "ok") { + throw new RuntimeException("create volume failed: $vol.message") + } + } + String sid = server.server.id + // wait upto 2 minutes for status to become ACTIVE + def status = "??" + (1..serverCreationTimeout/5).each { + if (status == "ACTIVE") return + sleep 5000 + def x = p.httpJsonTransaction([url : "${p.urls['nova']}/servers/$sid"]) +// if (debug) System.err.println "Waiting status=$x.server.status" + status = x.server.status + } + logger.info "Done waiting status=$status" + if (status != "ACTIVE") { + throw new RuntimeException("create server failed to get ACTIVE: ${object2json(server).toString(2)}") + } +// if (debug) System.err.println object2json(server).toString(2) + if (req.vmType.volumeSizeGB > 0) { +// if (debug) System.err.println object2json(vol.volume).toString(2) + def attach = [ + url : "${p.urls['nova']}/servers/$sid/os-volume_attachments", + method : "POST", + body : [ volumeAttachment: [volumeId: vol.volume.id, device: "/dev/vdc"]] + ] + server["volume"] = vol +// if (debug) System.err.println "attach request: ${object2json(attach).toString(2)}" + def a = p.httpJsonTransaction(attach,false) +// if (debug) System.err.println object2json(a).toString(2) + server["volume_attach"] = a + } + if (req.vmType.incomingSecurityRules.size() > 0 && loc.supportsSecurityGroups) { + def addSecurityGroup = [ + url : "${p.urls['nova']}/servers/$sid/action", + method : "POST", + body : [ addSecurityGroup: [name: "$req.vmType.name"]] + ] + def res4 = p.httpJsonTransaction(addSecurityGroup,false,true,true,false) + } + if (req.vmType.needPublicIp) { + sleep 10000 + server["ip"] = addFloatingIp(req.projectName, sid, req.floatingIp) + } + x["res"] = server +// if (debug) System.err.println object2json(server).toString(2) + return server + } + def createKeypair (String projectName, User user) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == projectName} + if (p == null) return "No project with name $projectName" + def req1 = [ + url : "${p.urls['nova']}/os-keypairs", + method : "POST", + body : [ keypair : [ + name: user.name, + public_key: user.publicKey] + ] + ] + def k = p.httpJsonTransaction(req1,false) +// if (debug) System.err.println k + return k + } + def addFloatingIp(String projectName,sid,requestIp) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == projectName} + if (p == null) return "No project with name $projectName" + def res = p.httpJsonTransaction([url : "${p.urls['nova']}/os-floating-ips"],false) + def ip = null +// if (debug) System.err.println object2json(res).toString(2) + res.floating_ips.each { x -> + if (debug) System.err.println "AAAAAAAAAA ${object2json(x).toString(2)} ${x['instance_id']}" + if (requestIp != null && requestIp != "$x.ip") return + if ("${x['instance_id']}" == "null") ip = x.ip + } + if (requestIp != null && ip == null) { + throw new RuntimeException("Unable to find requested floating IP: $requestIp") + } + if (ip == null) { + res = p.httpJsonTransaction([method:"POST", url : "${p.urls['nova']}/os-floating-ips"],false) +// if (debug) System.err.println object2json(res).toString(2) + ip = res.floating_ip.ip + if (debug) System.err.println "Using new ip: $ip" + } else { + if (debug) System.err.println "Using old ip: $ip" + } + def req = [ + url : "${p.urls['nova']}/servers/$sid/action", + method : "POST", + body : [ addFloatingIp: [address: ip]] + ] +// if (debug) System.err.println object2json(req).toString(2) + p.httpJsonTransaction(req,false,true,true,false) + return ip + } + def action(req) { + switch (req) { + case OpenstackRequestDelete: delete(req.projectName,req.objectType, req.objectName); break + case OpenstackRequestKeyPair: createKeypair(req.projectName,req.user); break + case OpenstackRequestFlavor: createFlavor(req.projectName,req.vmType); break + case OpenstackRequestSecurityGroup: createSecurityGroup(req.projectName,req.vmType); break + case OpenstackRequestNewServer: + def r = createServer(req) + logger.info "createServer returns: $r" + break; + } + } + Response serverAction(OpenstackRequestServerAction req) { + OpenStackControllerProject p + p = projects.find {OpenStackControllerProject p1 -> p1.tenantName == req.projectName} + if (p == null) { + logger.warn "serverAction with null $req.projectName" + throw new RuntimeException("no Openstack project $req.projectName") + } + Server s = p.project.servers.find { it.name == req.name } + if (s == null) { + logger.warn "serverAction unable to find VM with name $req.name in $req.projectName" + throw new RuntimeException("unable to find VM with name $req.name in $req.projectName") + } + def url = "${p.urls['nova']}/servers/$s.ID/action" + def res + switch (req.action) { + case "stop": + case "start": + res = p.httpJsonTransaction([url:url,method:"POST",body:["os-$req.action":"null"]],false,true,true,false) + break; + case "pause": + case "unpause": + case "suspend": + case "resume": + case "lock": + case "unlock": + res = p.httpJsonTransaction([url:url,method:"POST",body:["$req.action":"null"]],false,true,true,false) + break; + case "reboot": + res = p.httpJsonTransaction([url:url,method:"POST",body:[reboot:[type:"SOFT"]]],false,true,true,false) + break; + case "hardReboot": + res = p.httpJsonTransaction([url:url,method:"POST",body:[reboot:[type:"HARD"]]],false,true,true,false) + break; + case "reset-state": + res = p.httpJsonTransaction([url:url,method:"POST",body:['os-resetState':[state:"error"]]],false,true,true,false) + break; + case "reset-state-active": + res = p.httpJsonTransaction([url:url,method:"POST",body:['os-resetState':[state:"active"]]],false,true,true,false) + break; + default: + logger.error "unknown nova command $req.action"; + return + } + if (res == null) { + logger.warn "ran nova command $req.action on $req.name succesfully" + return + } + logger.error "ran nova command $req.action on $req.name: $res" + } + + OpenStackControllerProject findProject(String name) { + return projects.find {OpenStackControllerProject p1 -> p1.tenantName == name} + } + + +class OpenStackControllerProject { + HttpClient httpClient + def tokens = null + String password + String username + String tenantName + String tenantId + String keystoneUrl + OpenStackProject project + def urls = [:] + public OpenStackControllerProject(OpenStackProject project1) { + project = project1 + tenantName = project.name + username = project.adminUser + password = props.get("${loc.name}.password.$username".toString()) + if (password == null) { + // BSA setup + password = props.get("project.${project.name}.password".toString()) + } + if (password == null) { + logger.warn("No password for $loc.name $project.name") + System.err.println "No password for $loc.name $project.name $username" + } + password = JavaHttpClient.decryptPassword(password) + tenantId = project.tenantId + keystoneUrl = loc.keystoneUrl +// if (debug) System.err.println "u=$username pw=$password tenantId=$tenantId" + // Remove the refs are supported. +// logger.info "${server.ecore2json(loc, 100, null, false).toString(2)}".toString() +// if (debug) System.err.println "${server.ecore2json(project, 100, null, false).toString(2)}" + httpClient = new HttpClient() + httpClient.start(); + updateTokens() + } + public void poll() { +// updateImages() +// updateGeneric(loc,"image") +// updateGeneric(loc,"flavor") + updateServers() + updateHypervisor() + try { updateSecurityGroups() } catch (e) { println "updateSecurityGroups failed: $e" } + updateVolumes() + updateKeypairs() + if (urls['neutron'] != null) { + updateGenericNeutron(project, "network") + updateGenericNeutron(project, "subnet") + updateGenericNeutron(project, "router") + updateGenericNeutron(project, "port") + updateGenericNeutron(project, "security_group") + updateGenericNeutron(project, "floatingip") + } + server.save() + } + /** + * Polls the Openstack Nova API for all the nova state (e.g, servers, images,..) + * for a project. + */ + def synchronized void updateTokens() { + if (keystoneUrl == null) { + logger.warn("No keystoneUrl for $loc.name") + return + } + if (password == null) { + logger.warn("No password for $loc.name") + return + } + def req = [ url : "$keystoneUrl/tokens", method : "POST", + body :[ auth : [passwordCredentials : [username: username, password: password], tenantId: tenantId]] + ] + + // RACKSPACE + if (project.apiKey != null) { + req.body.auth = ["RAX-KSKEY:apiKeyCredentials" : [username: username, apiKey: JavaHttpClient.decryptPassword(project.apiKey)]] + } + // {"auth": {"RAX-KSKEY:apiKeyCredentials": {"username": "ft123456", "apiKey": "dae41ba7484d4f8482"}}} + + tokens = httpJsonTransaction(req,,false,false,false) + if (debug) System.err.println object2json(tokens).toString(2) + if (! tokens.access) { + if (debug) System.err.println "Unabled to access tenant: $tenantName : ${object2json(tokens).toString(2)}" + logger.fatal "Unabled to access tenant: $tenantName : ${object2json(tokens).toString(2)}" + return; + } + tokens.access.serviceCatalog.each { it -> + def u = it.endpoints.get(0).publicURL + it.endpoints.each { x -> + if (x['region'] == project.region) + u = x.publicURL + } + urls[it.name] = u + if (debug) System.err.println "serviceCatalog $it.name -> ${urls[it.name]}" + } + // RACKSPACE + [ images :"cloudImages", + nova:"cloudServersOpenStack", + neutron: "cloudNetworks", + cinder: "cloudBlockStorage"]. each { n, v -> + if (urls[n] == null && urls[v] != null) { + System.err.println "Using $v service for $n" + if (n == "neutron" && urls[v].endsWith("/v2.0")) + urls[v] = urls[v].replace("/v2.0","") + urls[n] = urls[v] + } + if (urls[n] != null) { + System.err.println "No $n service" + } + } + } + def void updateImages() { + def req1 = [url : "${urls['nova']}/images"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) + loc.images.clear() + res1.images.each { s -> + def req2 = [url : s.links[0].href] + def res2 = httpJsonTransaction(req2) + logger.debug(object2json(res2).toString(2)) + createFromJson(server, new Subject(loc, "images"),object2json(res2.image,true)) ; + } + } + def void updateKeypairs() { + def req1 = [url : "${urls['nova']}/os-keypairs"] + def res1 = httpJsonTransaction(req1) + project.keypairs.clear() + res1.keypairs.each { s -> + createFromJson(server, new Subject(project, "keypairs"),object2json(s.keypair,true)) ; + } + } + def void updateGeneric(o,name) { + def req1 = [url : "${urls['nova']}/${name}s"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) + o["${name}s"].clear() + res1["${name}s"].each { s -> +// logger.info("getting ${s.links[0].href}") + def req2 = [url : s.links[0].href] + def res2 = httpJsonTransaction(req2) +// logger.debug( object2json(res2).toString(2)) +// if (debug) System.err.println "o=$o name=$name" + createFromJson(server, new Subject(o, "${name}s"),object2json(res2[name],true)) ; + } + } + def void updateGenericNeutron(o,name) { + def req1 = [url : "${urls['neutron']}/v2.0/${name.replace('_','-')}s"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) + o["${name}s"].clear() + res1["${name}s"].each { s -> + if (s.tenant_id != null && s.tenant_id != tenantId) return + JSONObject json = OpenStackUtil.object2json(s,true) +// if (debug) System.err.println json.toString(2) +// if (debug) System.err.println "o=$o id=$s.id" + if (s.name == null || s.name == "" || name == "security_group") json.put("name", s.id) + createFromJson(server, new Subject(o, "${name}s"),json) ; + } + } + def createFromJson(ManagementServer server,subject,JSONObject json) { + server.create(subject,json,false) + } + + def void updateHypervisor() { + if (tenantName != "admin") return + def req1 = [url : "${urls['nova']}/os-hypervisors/detail"] + def res1 = null; + try { res1 = httpJsonTransaction(req1) } catch (e) { + logger.error "Unable to get Hypervisor information: $e" + return + } +// if (debug) System.err.println object2json(res1).toString(2) + loc.hypervisors.clear() + res1.hypervisors.each { s -> + def json = new JSONObject(s.cpu_info) + s.cpu_info = json2object(json) +// if (debug) System.err.println s +// if (debug) System.err.println object2json(s,true) +// logger.info(object2json(s).toString(2)) + createFromJson(server, new Subject(loc, "hypervisors"),object2json(s,true)) + } + loc.hypervisors.each { it.name = it.service.host } + } + + def void updateSecurityGroups() { + def req1 = [url : "${urls['nova']}/os-security-groups"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) + project.groups.clear() + res1.security_groups.each { s -> +// logger.info(object2json(s).toString(2)) + createFromJson(server, new Subject(project, "groups"),object2json(s,true)) + } + } + + def void updateVolumes() { + if (urls['cinder'] == null) return + def req1 = [url : "${urls['cinder']}/volumes/detail"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) +// System.out.println("VVVVVVVVVVVVVVVVVV poll res1: ${res1}") + project.volumes.clear() + res1.volumes.each { s -> +// logger.info(object2json(s).toString(2)) + createFromJson(server, new Subject(project, "volumes"),object2json(s,true)) + } + if (loc.version == OpenStackVersion.FOLSOM ) + project.volumes.each { it.name = it.display_name } + project.volumes.each { + if (it.name == null) it.name = it.display_name + } + } + + def void updateFloatingIps() { + def req1 = [url : "${urls['nova']}/os-floating-ips"] + def res1 = httpJsonTransaction(req1) +// if (debug) System.err.println object2json(res1).toString(2) + project.ips.clear() + res1.floating_ips.each { s -> +// logger.info(object2json(s).toString(2)) + createFromJson(server, new Subject(project, "ips"),object2json(s,true)) + } + } + def void updateServers() { + def req1 = [url : "${urls['nova']}/servers" ] + def res1 = httpJsonTransaction(req1) + project.servers.clear() +// if (debug) System.err.println OpenStackUtil.object2json(res1).toString(2) + res1.servers.each { s -> + def req2 = [url : s.links[0].href ] + def res2 = httpJsonTransaction(req2) +// if (debug) System.err.println OpenStackUtil.object2json(res2).toString(2) +// return +// logger.debug(object2json(res2).toString(2)) + if (res2.server.image == "") res2.server.image = null + createFromJson(server, new Subject(project, "servers"),object2json(res2.server,true)) +// def req3 = [url : res2.server.image.links[0].href] +// def res3 = httpJsonTransaction(req3) +// logger.debug( object2json(res3).toString(2)) +// def req4 = [url : res3.choices[0].links[0].href] +// def res4 = httpJsonTransaction(req4) +// logger.debug( object2json(res4).toString(2)) + } + } + def NeutronObject create(NeutronRequest req) { + def ename = req.eClass().name + def cname = ename.substring(6,ename.length()-7) + def name = cname.toLowerCase() + switch (req) { + case CreateNetworkRequest: name = "network"; cname = "Network"; break + } + JSONObject j = new JSONObject() + // TODO look at annotations in ecore2json + JSONObject jj = ManagementServer.ecore2json(req, 100, null, false) + jj.remove('$class') + jj.remove('projectName') + req.eClass().EAllAttributes.each { EAttribute attr -> + EAnnotation anno = attr.getEAnnotation("http://openecomp.org/sirius/openstack"); + if (anno == null) return + String name1 = anno.details.get("name") + if (name1 != null && jj.has(attr.name)) { + def v = jj.get(attr.name) + jj.remove(attr.name) + jj.put(name1, v) + } + name1 = anno.details.get("removeEmptyList") + if (name1 == "true" && jj.has(attr.name)) { + def v = jj.get(attr.name) + switch (v) { + case JSONArray: + if (v.length() == 0) jj.remove(attr.name) + } + } + } + req.eClass().EAllReferences.each { EReference ref -> + EAnnotation anno = ref.getEAnnotation("http://openecomp.org/sirius/openstack"); + if (anno == null) return + String name1 = anno.details.get("name") + if (name1 != null && jj.has(ref.name)) { + def v = jj.get(ref.name) + jj.remove(ref.name) + jj.put(name1, v) + } + name1 = anno.details.get("removeEmptyList") + if (name1 == "true" && jj.has(ref.name)) { + def v = jj.get(ref.name) + switch (v) { + case JSONArray: + if (v.length() == 0) jj.remove(ref.name) + } + } + } + j.put(name, jj) + if (debug) System.err.println jj.toString(2) + def req1 = [url : "${urls['neutron']}/v2.0/${name.replace('_','-')}s", method:"POST", body:j] + if (debug) System.err.println req1 + def res1 = httpJsonTransaction(req1) + if (debug) System.err.println res1 + j = OpenStackUtil.object2json(res1[name],true) + if (debug) System.err.println j == null ? "NULL" : j.toString(2) + return server.json2ecore(NeutronPackage.eINSTANCE.getEClassifier(cname),j, false) + } + def void deleteNeutron(String type, String name) { + project["${type}s"].each { o -> + if (o.name != name) return + logger.warn "deleting $type $name $o.id" + def req1 = [url : "${urls['neutron']}/v2.0/${type.replace('_','-')}s/$o.id", method:"DELETE"] + if (debug) System.err.println req1 + def res1 = httpJsonTransaction(req1,false,true,true,false) + if (debug) System.err.println res1 + } + } + def httpJsonTransaction(m, boolean polling = true, boolean throwError = true, boolean addAuth = true, boolean expectResponse = true) { + def exchange = new Exchange() + if (debug) System.err.println "m=$m" + def url = m.url ?: "${urls[m.api]}/$m.uri" + if (loc.bypassIp) { + url = url.replaceFirst("http://.*:", "http://$loc.bypassIp:") + } + exchange.setURL(url); + if (debug) System.err.println "URL=$url" + exchange.setMethod(m.method ?: "GET"); + def headers = m.headers ?: [:] + headers['Content-type'] = 'application/json' + if (addAuth) { + headers['X-Auth-Token'] = tokens.access.token.id + headers['X-Auth-Project-Id'] = project.tenantId + } + headers.each { n,v -> + exchange.addRequestHeader(n, v); + if (debug) System.err.println "Header: $n: $v" + } + if (m.body) { + String json = object2json(m.body).toString() +// logger.info("REQUEST: ${object2json(m.body).toString(2)}") + if (debug) System.err.println "REQUEST BODY: $json" +// exchange.setRequestContentSource(new ByteArrayInputStream(json.bytes)) + exchange.setRequestContent(new ByteArrayBuffer(json.bytes)); + } +// if (debug) System.err.println "Sending request" + Date start = new Date() + httpClient.send(exchange) +// if (debug) System.err.println "Exchange sent" + int exchangeState = exchange.waitForDone(); + if (debug) System.err.println "Exchange done: $exchangeState" + if (debug) System.err.println "code=$exchange.code content=$exchange.content" + if (polling) plogger.info "content=$exchange.content" + else logger.info "content=$exchange.content" + def res = exchange.content.toString() + if (res == "" && ! expectResponse) return null + if (res == "") { + def duration = new Date().time - start.time + throw new RuntimeException("Openstack API returned NULL: $url duration=$duration") + } + if (res.startsWith("401 Unauthorize") || res.startsWith("Authentication required")) { + // keystone auth expired + logger.info("keystone auth expired, updating tokens and resending request") + updateTokens(); + return httpJsonTransaction(m, polling, throwError, addAuth) + } + if (res.startsWith("404 Not Found")) { + if (throwError) + throw new RuntimeException("Openstack API: 404 Not Found") + return null + } + JSONObject json = new JSONObject(res) + if (exchange.code < 200 || exchange.code >= 300) { + if (throwError && json.has("expection")) { + throw new RuntimeException("Openstack API: expection $json") + } + if (throwError && json.has("badRequest")) { + throw new RuntimeException("Openstack API: badRequest $json") + } + if (throwError && json.has("itemNotFound")) { + throw new RuntimeException("Openstack API: itemNotFound $json") + } + if (throwError && json.has("NeutronError")) { + throw new RuntimeException("Openstack API: NeutronError $json") + } + if (throwError) { + throw new RuntimeException("Openstack API: Error $json") + } + } + + if (debug) System.err.println json.toString(2) +// logger.info("RETURNED: ${json.toString(2)}") + return json2object(json) + } + public void associateFloatingIp(String ipId, String portId) { + def req1 = [url : "${urls['neutron']}/v2.0/floatingips/$ipId", method:"PUT", body:[floatingip:[port_id:portId]]] + if (debug) System.err.println req1 + def res1 = httpJsonTransaction(req1,false,true,true,false) + if (debug) System.err.println res1 +// return server.json2ecore(NeutronPackage.eINSTANCE.getEClassifier(cname),j, false) + } + + +} + +private class Exchange extends HttpExchange { + String version2; + int code; + String message; + String location; + StringBuffer content = new StringBuffer(); + + protected void onResponseHeader(Buffer name, Buffer value) { +// System.out.println("HEADER: " + name + " " + value); + if (name.toString().equals("Location")) + location = value.toString(); + } + + protected void onResponseStatus(Buffer httpVersion, int statusCode, Buffer statusMessage) { +// if (debug) System.err.println "v=$httpVersion code=$statusCode m=$statusMessage" + version2 = httpVersion.toString(); + code = statusCode; + message = statusMessage.toString(); + } + protected void onResponseContent(Buffer content) { + this.content.append(content.toString()); + } + +} +} + |