def main(argv=None): '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by Jürgen Buchhammer on %s. Copyright 2016 IONOS. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-t', '--type', dest='metatype', default="OVF", help='type of VM meta data') parser.add_argument('-m', '--metadata', dest='metafile', required=True, default=None, help='meta data file') parser.add_argument('-d', '--datacenterid', dest='datacenterid', default=None, help='datacenter of the new server') parser.add_argument('-D', '--DCname', dest='dcname', default=None, help='new datacenter name') parser.add_argument('-l', '--location', dest='location', default=None, help='location for new datacenter') parser.add_argument('-v', '--verbose', dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = IonosEnterpriseService(user, password) if args.metatype == 'OVF': metadata = OFVData(args.metafile) metadata.parse() else: sys.stderr.write("Metadata type '{}' is not supported".format( args.metatype)) return 1 # we need the DC first to have the location defined dc_id = None if args.datacenterid is None: if args.dcname is None or args.location is None: sys.stderr.write( "Either '-d <id>' or '-D <name> -l <loc>' must be specified" ) return 1 # else: we will create the DC later after parsing the meta data else: dc_id = args.datacenterid if dc_id is None: location = args.location dc = Datacenter(name=args.dcname, location=location, description="created by pb_importVM") print("create new DC {}".format(str(dc))) response = pbclient.create_datacenter(dc) dc_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(result)) else: dc = pbclient.get_datacenter(dc_id) location = dc['properties']['location'] print("use existing DC {} in location {}".format( dc['properties']['name'], location)) # check if images exist for disk in metadata.disks: disk_name = disk['file'] images = get_disk_image_by_name(pbclient, location, disk_name) if not images: raise ValueError( "No HDD image with name '{}' found in location {}".format( disk_name, location)) if len(images) > 1: raise ValueError( "Ambigous image name '{}' in location {}".format( disk_name, location)) disk['image'] = images[0]['id'] # now we're ready to create the VM # Server server = Server(name=metadata.name, cores=metadata.cpus, ram=metadata.ram) print("create server {}".format(str(Server))) response = pbclient.create_server(dc_id, server) srv_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # NICs (note that createing LANs may be implicit) for nic in metadata.nics: dcnic = NIC(name=nic['nic'], lan=nic['lanid']) print("create NIC {}".format(str(dcnic))) response = pbclient.create_nic(dc_id, srv_id, dcnic) nic_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) response = pbclient.get_nic(dc_id, srv_id, nic_id, 2) mac = response['properties']['mac'] print("dcnic has MAC {} for {}".format(mac, nic_id)) # end for(nics) # Volumes (we use the image name as volume name too requests = [] for disk in metadata.disks: dcvol = Volume(name=disk['file'], size=disk['capacity'], image=disk['image'], licence_type=metadata.licenseType) print("create Volume {}".format(str(dcvol))) response = pbclient.create_volume(dc_id, dcvol) requests.append(response['requestId']) disk['volume_id'] = response['id'] # end for(disks) if requests: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) for disk in metadata.disks: print("attach volume {}".format(disk)) response = pbclient.attach_volume(dc_id, srv_id, disk['volume_id']) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(disks) print("import of VM succesfully finished") return 0 except KeyboardInterrupt: # handle keyboard interrupt return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2
def main(argv=None): '''Parse command line options and create a server/volume composite.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 IONOS. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-i', '--infile', dest='infile', default=None, required=True, help='the input file name') parser.add_argument('-D', '--DCname', dest='dcname', default=None, help='new datacenter name') # TODO: add/overwrite image password for creation # parser.add_argument('-P', '--imagepassword', dest='imgpassword', # default=None, help='the image password') parser.add_argument('-v', '--verbose', dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = IonosEnterpriseService(user, password) usefile = args.infile print("read dc from {}".format(usefile)) dcdef = read_dc_definition(usefile) if verbose > 0: print("using DC-DEF {}".format(json.dumps(dcdef))) # setup dc: # + create empty dc # + create volumes (unattached), map uuid to servers (custom dict) # + create servers # + create nics # + attach volumes if 'custom' in dcdef and 'id' in dcdef['custom']: dc_id = dcdef['custom']['id'] print("using existing DC w/ id {}".format(str(dc_id))) else: if args.dcname is not None: print("Overwrite DC name w/ '{}'".format(args.dcname)) dcdef['properties']['name'] = args.dcname dc = getDatacenterObject(dcdef) # print("create DC {}".format(str(dc))) response = pbclient.create_datacenter(dc) dc_id = response['id'] if 'custom' not in dcdef: dcdef['custom'] = dict() dcdef['custom']['id'] = dc_id result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(result)) tmpfile = usefile + ".tmp_postdc" write_dc_definition(dcdef, tmpfile) requests = [] print("create Volumes {}".format(str(dc))) # we do NOT consider dangling volumes, only server-attached ones for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes".format( server['properties']['name'])) continue for volume in server['entities']['volumes']['items']: if 'custom' in volume and 'id' in volume['custom']: vol_id = volume['custom']['id'] print("using existing volume w/ id {}".format(str(vol_id))) else: dcvol = getVolumeObject(volume) print("OBJ: {}".format(str(dcvol))) response = pbclient.create_volume(dc_id, dcvol) volume.update({'custom': {'id': response['id']}}) requests.append(response['requestId']) # end for(volume) # end for(server) if requests: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) tmpfile = usefile + ".tmp_postvol" write_dc_definition(dcdef, tmpfile) else: print("all volumes existed already") requests = [] print("create Servers {}".format(str(dc))) # we do NOT consider dangling volumes, only server-attached ones for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'custom' in server and 'id' in server['custom']: srv_id = server['custom']['id'] print("using existing server w/ id {}".format(str(srv_id))) else: dcsrv = getServerObject(server) print("OBJ: {}".format(str(dcsrv))) response = pbclient.create_server(dc_id, dcsrv) server.update({'custom': {'id': response['id']}}) requests.append(response['requestId']) # end for(server) if requests: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) tmpfile = usefile + ".tmp_postsrv" write_dc_definition(dcdef, tmpfile) else: print("all servers existed already") # TODO: only do this if we have lan entities requests = [] # Huuh, looks like we get unpredictable order for LANs! # Nope, order of creation determines the LAN id, # thus we better wait for each request print("create LANs {}".format(str(dc))) for lan in dcdef['entities']['lans']['items']: print("- lan {}".format(lan['properties']['name'])) dclan = getLANObject(lan) print("OBJ: {}".format(str(dclan))) response = pbclient.create_lan(dc_id, dclan) lan.update({'custom': {'id': response['id']}}) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(lan) tmpfile = usefile + ".tmp_postlan" write_dc_definition(dcdef, tmpfile) requests = [] # Attention: # NICs appear in OS in the order, they are created. # But DCD rearranges the display by ascending MAC addresses. # This does not change the OS order. # MAC may not be available from create response, # thus we wait for each request :-( print("create NICs {}".format(str(dc))) for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) srv_id = server['custom']['id'] if 'nics' not in server['entities']: print(" server {} has no NICs".format( server['properties']['name'])) continue macmap = dict() for nic in server['entities']['nics']['items']: dcnic = getNICObject(nic) response = pbclient.create_nic(dc_id, srv_id, dcnic) # print("dcnic response {}".format(str(response))) # mac = response['properties']['mac'] # we don't get it here !? nic_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) response = pbclient.get_nic(dc_id, srv_id, nic_id, 2) mac = response['properties']['mac'] print("dcnic has MAC {} for {}".format(mac, nic_id)) macmap[mac] = nic_id # end for(nic) macs = sorted(macmap) print("macs will be displayed by DCD in th following order:") for mac in macs: print("mac {} -> id{}".format(mac, macmap[mac])) # end for(server) tmpfile = usefile + ".tmp_postnic" write_dc_definition(dcdef, tmpfile) requests = [] # don't know if we get a race here too, so better wait for each request :-/ print("attach volumes {}".format(str(dc))) for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes".format( server['properties']['name'])) continue srv_id = server['custom']['id'] for volume in server['entities']['volumes']['items']: print("OBJ: {}".format(volume['properties']['name'])) response = pbclient.attach_volume(dc_id, srv_id, volume['custom']['id']) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(volume) # end for(server) tmpfile = usefile + ".tmp_postatt" write_dc_definition(dcdef, tmpfile) # TODO: do we need to set boot volume for each server? # looks like it's working without return 0