def createUser(self, username, password, emailaddress, groups, domain, provider=None, protected=False): username, _, userprovider = username.partition('@') if userprovider and userprovider != provider: raise exceptions.BadRequest('Username may not contain @ unless suffix matches provider') if provider: username = "******".format(username, provider) if self.osisuser.search({'id': username})[1:]: raise exceptions.Conflict('Username %s already exists.' % username) if not emailaddress: raise exceptions.BadRequest('Email address cannot be empty.') else: if len(emailaddress) != 1: raise exceptions.BadRequest('Only 1 email address is allowed for each user.') if not self._isValidEmailAddress(emailaddress[0]): raise exceptions.BadRequest('Email address %s is in an invalid format.' % emailaddress[0]) # only require unique email for users that dont have a provider set if not provider: emailusers = self.osisuser.search({'emails': emailaddress})[1:] for user in emailusers: if '@' not in user['id']: raise exceptions.Conflict('Email address %s is already registered in the ' 'system.' % emailaddress[0]) user = self.osisuser.new() user.id = username user.groups = groups user.emails = emailaddress user.domain = domain user.protected = protected if not password: password = str(random.random()) elif not self._isValidPassword(password): raise exceptions.BadRequest("Password should have at least 8 characters and not more " "than 60 characters.") user.passwd = j.tools.hash.md5_string(password) try: self.osisuser.set(user) return user.id except RemoteException as e: if e.eco.exceptionclassname == "ValueError": raise exceptions.BadRequest(json.loads(e.eco.exceptioninfo)['message']) else: raise
def updateUser(self, username, password, emailaddress, groups, domain=None): users = self.osisuser.search({'id': username})[1:] if not users: raise exceptions.NotFound('Could not find user with the username: %s.' % username) else: user = self.osisuser.get(users[0]['guid']) if password: if not self._isValidPassword(password): raise exceptions.BadRequest("Password should have at least 8 characters and not " "more than 60 characters.") else: user.passwd = j.tools.hash.md5_string(password) if emailaddress and emailaddress != ['']: if not self._isValidEmailAddress(emailaddress[0]): raise exceptions.BadRequest('Email address %s is in an invalid format.' % emailaddress[0]) if emailaddress != user.emails and self.osisuser.search({'emails': emailaddress})[1:]: raise exceptions.Conflict('Email address %s is already registered in the ' 'system with a different username.' % emailaddress[0]) user.emails = emailaddress user.groups = groups if domain: user.domain = domain self.osisuser.set(user) return True
def registerNetworkIdRange(self, gid, start, end, **kwargs): """ Add a new network idrange param:start start of the range param:end end of the range result """ newrange = set(range(int(start), int(end) + 1)) if self._models.networkids.exists(gid): cloudspaces = self.cbmodel.cloudspace.search({ '$fields': ['networkId'], '$query': { 'status': { '$in': ['DEPLOYED', 'VIRTUAL'] }, 'gid': gid } })[1:] usednetworkids = {space['networkId'] for space in cloudspaces} if usednetworkids.intersection(newrange): raise exceptions.Conflict( "Atleast one networkId conflicts with deployed networkids") self._models.networkids.updateSearch( {'id': gid}, {'$addToSet': { 'networkids': { '$each': newrange } }}) else: networkids = {'id': gid, 'networkids': newrange} self._models.networkids.set(networkids) return True
def addExternalIPS(self, externalnetworkId, startip, endip, **kwargs): """ Add public ips to an existing range """ if not self.models.externalnetwork.exists(externalnetworkId): raise exceptions.NotFound( "Could not find external network with id %s" % externalnetworkId) pool = self.models.externalnetwork.get(externalnetworkId) try: net = netaddr.IPNetwork("{}/{}".format(pool.network, pool.subnetmask)) if netaddr.IPAddress(startip) not in net: raise exceptions.BadRequest( "Start IP Addresses %s is not in subnet %s" % (startip, net)) if netaddr.IPAddress(endip) not in net: raise exceptions.BadRequest( "End IP Addresses %s is not in subnet %s" % (endip, net)) except netaddr.AddrFormatError as e: raise exceptions.BadRequest(e.message) ips = set(pool.ips) newset = {str(ip) for ip in netaddr.IPRange(startip, endip)} usedips = self._getUsedIPS(pool) duplicateips = usedips.intersection(newset) if duplicateips: raise exceptions.Conflict( "New range overlaps with existing deployed IP Addresses") ips.update(newset) pool.ips = list(ips) self.models.externalnetwork.set(pool) return True
def registerNetworkIdRange(self, gid, start, end, **kwargs): """ Add a new network idrange param:start start of the range param:end end of the range result """ newrange = set(range(int(start), int(end) + 1)) if self._models.networkids.exists(gid): cloudspaces = self.cbmodel.cloudspace.search( { "$fields": ["networkId"], "$query": {"status": {"$in": ["DEPLOYED", "VIRTUAL"]}, "gid": gid}, } )[1:] usednetworkids = {space["networkId"] for space in cloudspaces} if usednetworkids.intersection(newrange): raise exceptions.Conflict( "Atleast one networkId conflicts with deployed networkids" ) self._models.networkids.updateSearch( {"id": gid}, {"$addToSet": {"networkids": {"$each": newrange}}} ) else: networkids = {"id": gid, "networkids": newrange} self._models.networkids.set(networkids) return True
def updateByPort(self, cloudspaceId, sourcePublicIp, sourcePublicPort, sourceProtocol, publicIp, publicPort, machineId, localPort, protocol=None, **kwargs): protocol = protocol or 'tcp' cloudspace, machine, fw, publicIp = self._validate_forward( cloudspaceId, publicIp, publicPort, machineId, localPort, protocol) fw_id, fw_gid = self._getFirewallId(cloudspaceId) if not self.netmgr.fw_check(fw_id, timeout=5): raise exceptions.ServiceUnavailable( 'Can not update PortForward at this time') forwards = self.netmgr.fw_forward_list(fw_id, fw_gid) for fw in forwards: if fw['publicIp'] == sourcePublicIp and int(fw['publicPort']) == sourcePublicPort and \ fw['protocol'] == sourceProtocol: forwards.remove(fw) break if self._selfcheckduplicate(forwards, publicIp, publicPort, protocol): raise exceptions.Conflict( "Forward for %s with port %s already exists" % (publicIp, publicPort)) self._deleteByPort(cloudspaceId, sourcePublicIp, sourcePublicPort, sourceProtocol) return self.create(cloudspaceId, publicIp, publicPort, machineId, localPort, protocol)
def assertName(self, cloudspaceId, name): if not name or not name.strip(): raise ValueError("Machine name can not be empty") results = models.vmachine.search({'cloudspaceId': cloudspaceId, 'name': name, 'status': {'$ne': resourcestatus.Machine.DESTROYED}})[1:] if results: raise exceptions.Conflict('Selected name already exists')
def assertName(self, cloudspaceId, name): if not name or not name.strip(): raise ValueError("Machine name can not be empty") results = models.vmachine.search({ "cloudspaceId": cloudspaceId, "name": name, "status": { "$ne": resourcestatus.Machine.DESTROYED }, })[1:] if results: raise exceptions.Conflict("Selected name already exists")
def fw_forward_create(self, fwid, gid, fwip, fwport, destip, destport, protocol="tcp", **kwargs): """ param:fwid firewall id param:gid grid id param:fwip str,,adr on fw which will be visible to extenal world param:fwport str,,port on fw which will be visble to external world param:destip adr where we forward to e.g. a ssh server in DMZ param:destport port where we forward to e.g. a ssh server in DMZ """ protocol = protocol or "tcp" with self.osisvfw.lock(fwid): fwobj = self._getVFWObject(fwid) for tcprule in fwobj.tcpForwardRules: if (tcprule.fromAddr == fwip and tcprule.fromPort == str(fwport) and tcprule.protocol == protocol): raise exceptions.Conflict( "Forward to %s with port %s already exists" % (fwip, fwport)) rule = fwobj.new_tcpForwardRule() rule.fromAddr = fwip rule.fromPort = str(fwport) rule.toAddr = destip rule.toPort = str(destport) rule.protocol = protocol self.osisvfw.updateSearch({"guid": fwid}, {"$addToSet": { "tcpForwardRules": rule }}) args = { "name": "%s_%s" % (fwobj.domain, fwobj.name), "fwobject": fwobj.obj2dict(), } result = self._applyconfig(fwobj.gid, fwobj.nid, args) if not result: self.fw_forward_delete(fwid, gid, fwip, fwport, destip, destport, protocol, apply=False) return result
def update(self, cloudspaceId, id, publicIp, publicPort, machineId, localPort, protocol, **kwargs): """ Update a port forwarding rule :param cloudspaceId: id of the cloudspace :param id: id of the portforward to edit :param publicIp: public ipaddress :param publicPort: public port :param machineId: id of the virtual machine :param localPort: local port :param protocol: protocol udp or tcp """ machineId = int(machineId) cloudspaceId = int(cloudspaceId) cloudspace = self.models.cloudspace.get(cloudspaceId) fw = self.netmgr.fw_list(cloudspace.gid, cloudspaceId) if len(fw) == 0: raise exceptions.NotFound( 'Incorrect cloudspace or there is no corresponding gateway') fw_id = fw[0]['guid'] if not self.netmgr.fw_check(fw_id, timeout=5): raise exceptions.ServiceUnavailable( 'Can not update PortForward at this time') forwards = self.netmgr.fw_forward_list(fw_id, cloudspace.gid) id = int(id) if not id < len(forwards): raise exceptions.NotFound('Cannot find the rule with id %s' % str(id)) forward = forwards[id] machine = self.models.vmachine.get(machineId) if machine.nics: if machine.nics[0].ipAddress != 'Undefined': localIp = machine.nics[0].ipAddress else: raise exceptions.NotFound( 'No correct ipaddress found for machine with id %s' % machineId) self.netmgr.fw_forward_delete(fw_id, cloudspace.gid, forward['publicIp'], forward['publicPort'], forward['localIp'], forward['localPort'], forward['protocol']) forwards = self.netmgr.fw_forward_list(fw_id, cloudspace.gid) if self._selfcheckduplicate(forwards, publicIp, publicPort, protocol): raise exceptions.Conflict( "Forward for %s with port %s already exists" % (publicIp, publicPort)) self.netmgr.fw_forward_create(fw_id, cloudspace.gid, publicIp, publicPort, localIp, localPort, protocol) forwards = self.netmgr.fw_forward_list(fw_id, cloudspace.gid) return self._process_list(forwards, cloudspaceId)
def add(self, name, gid, locationcode, **kwargs): location = next(iter(self.models.location.search({"gid": gid})[1:]), None) if location: raise exceptions.Conflict("Location with gid %s already exists" % gid) location = self.models.location.new() location.gid = gid location.flag = "black" location.locationCode = locationcode location.name = name self.models.location.set(location) return "Location has been added successfully, do not forget to add networkids and public IPs"
def delete(self, diskId, detach, permanently=False, name=None, reason=None, **kwargs): """ Delete a disk :param diskId: id of disk to delete :param detach: detach disk from machine first :return True if disk was deleted successfully """ if not self.models.disk.exists(diskId): return True disk = self.models.disk.get(diskId) if name and disk.name != name: raise exceptions.BadRequest('Incorrect disk name specified') if disk.status == resourcestatus.Disk.DESTROYED: return True if disk.type == 'C': machines = self.models.vmachine.count({'tags': {'$regex': ".*cdrom:%s.*" % disk.id}, 'status': {'$ne': resourcestatus.Machine.DESTROYED}}) if machines: raise exceptions.Conflict('Cannot delete a used disk') else: machines = self.models.vmachine.search({'disks': diskId, 'status': {'$ne': resourcestatus.Machine.DESTROYED}})[1:] if machines and not detach: raise exceptions.Conflict('Can not delete disk which is attached') elif machines: j.apps.cloudapi.machines.detachDisk(machineId=machines[0]['id'], diskId=diskId, **kwargs) disk.status = resourcestatus.Disk.CREATED disk.deletionTime = int(time.time()) if not machines and not permanently: self.models.disk.updateSearch({'id': diskId}, {'$set': {'status': resourcestatus.Disk.TOBEDELETED}}) return True disk.status = resourcestatus.Disk.DELETED self.models.disk.set(disk) provider = self.cb.getProviderByGID(disk.gid) volume = self.getStorageVolume(disk, provider) provider.destroy_volume(volume) disk.status = resourcestatus.Disk.DESTROYED self.models.disk.set(disk) return True
def addExternalNetwork(self, name, subnet, gateway, startip, endip, gid, vlan, accountId, pingips, dhcpServerId, **kwargs): """ Adds a public network range to be used for cloudspaces param:subnet the subnet to add in CIDR notation (x.x.x.x/y) """ gateway = gateway.strip() try: net = netaddr.IPNetwork(subnet) if netaddr.IPAddress(startip) not in net: raise exceptions.BadRequest( "Start IP Addresses %s is not in subnet %s" % (startip, subnet)) if netaddr.IPAddress(endip) not in net: raise exceptions.BadRequest( "End IP Addresses %s is not in subnet %s" % (endip, subnet)) if not checkIPS(net, [gateway]): raise exceptions.BadRequest( "Gateway Address %s is not in subnet %s" % (gateway, subnet)) if self.models.externalnetwork.count({'vlan': vlan}) > 0: raise exceptions.Conflict( "VLAN {} is already in use by another external network") except netaddr.AddrFormatError as e: raise exceptions.BadRequest(e.message) if dhcpServerId < 0 and type(dhcpServerId) == int: raise exceptions.BadRequest( "DHCP server ID is not valid! Use only 0 or positive number") if not dhcpServerId: dhcpServerId = 0 if pingips is None: pingips = '8.8.8.8' pingips = self._checkPingIps(pingips) pool = self.models.externalnetwork.new() pool.gid = int(gid) pool.gateway = gateway pool.name = name pool.pingips = pingips pool.vlan = vlan or 0 pool.subnetmask = str(net.netmask) pool.network = str(net.network) pool.accountId = accountId or 0 pool.dhcpServerId = dhcpServerId pool.ips = [str(ip) for ip in netaddr.IPRange(startip, endip)] pool.id, _, _ = self.models.externalnetwork.set(pool) return pool.id
def tag(self, machineId, tagName, **kwargs): """ Adds a tag to a machine, useful for indexing and following a (set of) machines param:machineId id of the machine to tag param:tagName tag """ vmachine = self._validateMachineRequest(machineId) tags = j.core.tags.getObject(vmachine.tags) if tags.labelExists(tagName): raise exceptions.Conflict( 'Tag %s is already assigned to this vMachine' % tagName) tags.labelSet(tagName) vmachine.tags = tags.tagstring self.models.vmachine.set(vmachine) return True
def deleteExternalNetwork(self, externalnetworkId, **kwargs): if not self.models.externalnetwork.exists(externalnetworkId): raise exceptions.NotFound( "Could not find external network with id %s" % externalnetworkId) cloudCount = self.models.cloudspace.count({ "externalnetworkId": externalnetworkId, "status": { "$ne": "DESTROYED" } }) if cloudCount == 0: self.models.externalnetwork.delete(externalnetworkId) else: raise exceptions.Conflict("Cannot delete, external network in use") return True
def createGroup(self, name, domain, description, **args): """ create a group param:name name of group param:domain of group param:description of group result bool """ if self.modelGroup.search({'id': name})[1:]: raise exceptions.Conflict("Group with name %s already exists" % name) group = self.modelGroup.new() group.id = name group.domain = domain group.description = description self.modelGroup.set(group) return True
def create( self, cloudspaceId, publicIp, publicPort, machineId, localPort, protocol=None, **kwargs ): """ Create a port forwarding rule :param cloudspaceId: id of the cloudspace :param publicIp: public ipaddress :param publicPort: public port :param machineId: id of the virtual machine :param localPort: local port on vm :param protocol: protocol udp or tcp """ cloudspace, machine, fw, publicIp = self._validate_forward( cloudspaceId, publicIp, publicPort, machineId, localPort, protocol ) fw_id = fw["guid"] if not self.netmgr.fw_check(fw_id, timeout=5): raise exceptions.ServiceUnavailable( "Can not create PortForward at this time" ) grid_id = fw["gid"] localIp = self._getLocalIp(machine) forwards = self.netmgr.fw_forward_list(fw_id, cloudspace.gid) if self._selfcheckduplicate(forwards, publicIp, publicPort, protocol): raise exceptions.Conflict( "Forward to %s with port %s already exists" % (publicIp, publicPort) ) try: result = self.netmgr.fw_forward_create( fw_id, grid_id, publicIp, publicPort, localIp, localPort, protocol ) except: raise exceptions.ServiceUnavailable( "Forward to %s with port %s failed to create." % (publicIp, publicPort) ) return result
def deleteImage(self, imageId, permanently): image = self.models.image.get(imageId) if image.status == resourcestatus.Image.DESTROYED: return True references = self.models.vmachine.count({'imageId': imageId, 'status': {'$ne': resourcestatus.Machine.DESTROYED}}) if references and permanently: raise exceptions.Conflict("Can not delete an image which is still used") if image.status != resourcestatus.Image.DELETED: if image.status != resourcestatus.Image.CREATED: raise exceptions.Forbidden("Can not delete an image which is not created yet.") deleted_state = resourcestatus.Image.DELETED if permanently: deleted_state = resourcestatus.Image.DESTROYED provider = self.cb.getProviderByGID(image.gid) provider.ex_delete_template(image.referenceId) self.models.image.updateSearch({'id': imageId}, {'$set': {'status': deleted_state, 'deletionTime': int(time.time())}}) self.models.stack.updateSearch({'images': imageId}, {'$pull': {'images': imageId}}) return True
def create(self, name, username, emailaddress, maxMemoryCapacity=-1, maxVDiskCapacity=-1, maxCPUCapacity=-1, maxNetworkPeerTransfer=-1, maxNumPublicIP=-1, sendAccessEmails=True, **kwargs): with self.models.account.lock(name): if sendAccessEmails == 1: sendAccessEmails = True elif sendAccessEmails == 0: sendAccessEmails = False accounts = self.models.account.search({ "name": name, "status": { "$ne": resourcestatus.Account.DESTROYED } })[1:] if accounts: raise exceptions.Conflict("Account name is already in use.") if j.core.portal.active.auth.userExists(username): if (emailaddress and not self.syscl.user.search({ "id": username, "emails": emailaddress })[1:]): raise exceptions.Conflict( "The specified username and email do not match.") user = j.core.portal.active.auth.getUserInfo(username) emailaddress = user.emails[0] if user.emails else None else: if not emailaddress: raise exceptions.BadRequest( "Email address is required for new users.") Validators.EMAIL(emailaddress) password = j.base.idgenerator.generateGUID() j.apps.cloudbroker.user.create( username=username, emailaddress=[emailaddress], password=password, groups=["user"], ) now = int(time.time()) locationurl = j.apps.cloudapi.locations.getUrl().strip("/") account = self.models.account.new() account.name = name account.creationTime = now account.updateTime = now account.company = "" account.companyurl = "" account.status = resourcestatus.Account.CONFIRMED account.sendAccessEmails = sendAccessEmails resourcelimits = { "CU_M": maxMemoryCapacity, "CU_D": maxVDiskCapacity, "CU_C": maxCPUCapacity, "CU_NP": maxNetworkPeerTransfer, "CU_I": maxNumPublicIP, } self.cb.fillResourceLimits(resourcelimits) account.resourceLimits = resourcelimits ace = account.new_acl() ace.userGroupId = username ace.type = "U" ace.right = "CXDRAU" ace.status = "CONFIRMED" accountid = self.models.account.set(account)[0] mail_args = { "account": name, "username": username, "email": emailaddress, "portalurl": locationurl, } kwargs["ctx"].env["tags"] += " accountId:{}".format(accountid) if emailaddress: _send_signup_mail(hrd=self.hrd, **mail_args) return accountid
def create(self, name, username, emailaddress, maxMemoryCapacity=-1, maxVDiskCapacity=-1, maxCPUCapacity=-1, maxNetworkPeerTransfer=-1, maxNumPublicIP=-1, sendAccessEmails=True, **kwargs): with self.models.account.lock(name): if sendAccessEmails == 1: sendAccessEmails = True elif sendAccessEmails == 0: sendAccessEmails = False accounts = self.models.account.search({ 'name': name, 'status': { '$ne': resourcestatus.Account.DESTROYED } })[1:] if accounts: raise exceptions.Conflict('Account name is already in use.') if j.core.portal.active.auth.userExists(username): if emailaddress and not self.syscl.user.search( { 'id': username, 'emails': emailaddress })[1:]: raise exceptions.Conflict( 'The specified username and email do not match.') user = j.core.portal.active.auth.getUserInfo(username) emailaddress = user.emails[0] if user.emails else None else: if not emailaddress: raise exceptions.BadRequest( 'Email address is required for new users.') Validators.EMAIL(emailaddress) password = j.base.idgenerator.generateGUID() j.apps.cloudbroker.user.create(username=username, emailaddress=[emailaddress], password=password, groups=['user']) now = int(time.time()) locationurl = j.apps.cloudapi.locations.getUrl().strip('/') account = self.models.account.new() account.name = name account.creationTime = now account.updateTime = now account.company = '' account.companyurl = '' account.status = resourcestatus.Account.CONFIRMED account.sendAccessEmails = sendAccessEmails resourcelimits = { 'CU_M': maxMemoryCapacity, 'CU_D': maxVDiskCapacity, 'CU_C': maxCPUCapacity, 'CU_NP': maxNetworkPeerTransfer, 'CU_I': maxNumPublicIP } self.cb.fillResourceLimits(resourcelimits) account.resourceLimits = resourcelimits ace = account.new_acl() ace.userGroupId = username ace.type = 'U' ace.right = 'CXDRAU' ace.status = 'CONFIRMED' accountid = self.models.account.set(account)[0] mail_args = { 'account': name, 'username': username, 'email': emailaddress, 'portalurl': locationurl } kwargs['ctx'].env['tags'] += " accountId:{}".format(accountid) if emailaddress: _send_signup_mail(hrd=self.hrd, **mail_args) return accountid